From 065b77db84861a95d7c5f54a27604214e34981af Mon Sep 17 00:00:00 2001 From: NguyenAnhQuan Date: Fri, 24 Oct 2025 22:49:46 +0700 Subject: [PATCH] feat: 241025/login_by_seed_phrase --- package.json | 4 + src/api/config/index.ts | 31 +- src/api/neptuneApi.ts | 53 ++ src/api/request.ts | 14 + src/components/WalletInfo.vue | 192 ++++-- src/components/auth/CreateWalletComponent.vue | 639 ------------------ src/components/common/SpinnerCommon.vue | 52 ++ src/components/index.ts | 22 +- src/composables/useNeptuneWallet.ts | 292 ++++++++ src/interface/index.ts | 1 + src/interface/neptune.ts | 20 + src/router/route.ts | 31 +- src/stores/index.ts | 1 + src/stores/neptuneStore.ts | 113 ++++ src/types/neptune-wasm.d.ts | 39 ++ src/views/Auth/AuthView.vue | 2 +- .../Auth/components}/ConfirmSeedComponent.vue | 258 +++---- src/views/Auth/components/ConfirmTab.vue | 2 +- src/views/Auth/components/CreateTab.vue | 2 +- .../Auth/components/CreateWalletComponent.vue | 176 +++++ .../components}/ImportWalletComponent.vue | 2 +- .../components}/KeystoreDownloadComponent.vue | 10 +- src/views/Auth/components/LoginTab.vue | 2 +- .../Auth/components}/OnboardingComponent.vue | 0 .../Auth/components}/OpenWalletComponent.vue | 26 +- .../components}/RecoverySeedComponent.vue | 170 +++-- src/views/Auth/components/RecoveryTab.vue | 2 +- src/views/Auth/components/index.ts | 13 + .../steps/ChooseBackupMethodStep.vue | 46 ++ .../components/steps/CreatePasswordStep.vue | 384 +++++++++++ .../components/steps/WalletCreatedStep.vue | 125 ++++ src/views/Auth/components/steps/index.ts | 3 + src/views/Home/HomeView.vue | 8 +- src/views/Home/components/NetworkTab.vue | 245 +++---- src/views/Home/components/UTXOTab.vue | 4 +- src/views/Home/components/WalletTab.vue | 22 +- vite.config.ts | 3 + yarn.lock | 209 +++--- 38 files changed, 1940 insertions(+), 1278 deletions(-) create mode 100644 src/api/neptuneApi.ts create mode 100644 src/api/request.ts delete mode 100644 src/components/auth/CreateWalletComponent.vue create mode 100644 src/components/common/SpinnerCommon.vue create mode 100644 src/composables/useNeptuneWallet.ts create mode 100644 src/interface/neptune.ts create mode 100644 src/stores/neptuneStore.ts create mode 100644 src/types/neptune-wasm.d.ts rename src/{components/auth => views/Auth/components}/ConfirmSeedComponent.vue (66%) create mode 100644 src/views/Auth/components/CreateWalletComponent.vue rename src/{components/auth => views/Auth/components}/ImportWalletComponent.vue (99%) rename src/{components/auth => views/Auth/components}/KeystoreDownloadComponent.vue (98%) rename src/{components/auth => views/Auth/components}/OnboardingComponent.vue (100%) rename src/{components/auth => views/Auth/components}/OpenWalletComponent.vue (96%) rename src/{components/auth => views/Auth/components}/RecoverySeedComponent.vue (51%) create mode 100644 src/views/Auth/components/steps/ChooseBackupMethodStep.vue create mode 100644 src/views/Auth/components/steps/CreatePasswordStep.vue create mode 100644 src/views/Auth/components/steps/WalletCreatedStep.vue create mode 100644 src/views/Auth/components/steps/index.ts diff --git a/package.json b/package.json index 4000caa..6c5122a 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,9 @@ "version": "0.0.0", "private": true, "type": "module", + "workspaces": [ + "packages/*" + ], "scripts": { "dev": "vite", "build": "run-p type-check \"build-only {@}\" --", @@ -13,6 +16,7 @@ "format": "prettier --write src/" }, "dependencies": { + "@neptune/wasm": "file:./packages/neptune-wasm", "ant-design-vue": "^4.2.6", "axios": "^1.6.8", "dayjs": "^1.11.10", diff --git a/src/api/config/index.ts b/src/api/config/index.ts index 0f92372..e160c41 100644 --- a/src/api/config/index.ts +++ b/src/api/config/index.ts @@ -1,41 +1,22 @@ import axios from 'axios' -import router from '@/router' -import { STATUS_CODE_SUCCESS, ACCESS_TOKEN, STATUS_CODE_UNAUTHORIZED } from '@/utils' axios.defaults.withCredentials = false export const API_URL = import.meta.env.VITE_APP_API const instance = axios.create({ baseURL: API_URL, -}) - -instance.interceptors.request.use( - function (config: any) { - try { - const token = localStorage.getItem(ACCESS_TOKEN) - if (token) { - config.headers['Authorization'] = `Bearer ${token}` - } - } catch (error) { - throw Error('') - } - return config + headers: { + 'Content-Type': 'application/json', }, - function (error) { - return Promise.reject(error) - } -) + method: 'POST', +}) instance.interceptors.response.use( function (response) { - if (response?.status !== STATUS_CODE_SUCCESS) return Promise.reject(response?.data) - return response.data + // if (response?.status !== STATUS_CODE_SUCCESS) return Promise.reject(response?.data) + return response }, function (error) { - if (error?.response?.status === STATUS_CODE_UNAUTHORIZED || error.code === 'ERR_NETWORK') { - localStorage.clear() - return router.push({ name: 'login' }) - } if (error?.response?.data) { return Promise.reject(error?.response?.data) } diff --git a/src/api/neptuneApi.ts b/src/api/neptuneApi.ts new file mode 100644 index 0000000..825dd57 --- /dev/null +++ b/src/api/neptuneApi.ts @@ -0,0 +1,53 @@ +import { callJsonRpc } from '@/api/request' + +export const getUtxosFromViewKey = ( + viewKey: string, + startBlock: number = 0, + endBlock: number | null = null, + maxSearchDepth: number = 1000 +) => { + const params = { + viewKey, + startBlock, + endBlock, + maxSearchDepth, + } + return callJsonRpc('wallet_getUtxosFromViewKey', params) +} + +export const getBalance = async (): Promise => { + return await callJsonRpc('wallet_balance', []) +} + +export const getBlockHeight = async (): Promise => { + return await callJsonRpc('chain_height', []) +} + +export const getNetworkInfo = async (): Promise => { + return await callJsonRpc('node_network', []) +} + +export const getWalletAddress = async (): Promise => { + return await callJsonRpc('wallet_address', []) +} + +export const sendTransaction = async ( + toAddress: string, + amount: string, + fee: string +): Promise => { + const params = [toAddress, amount, fee] + return await callJsonRpc('wallet_send', params) +} + +export const broadcastSignedTransaction = async (signedTxData: any): Promise => { + return await callJsonRpc('wallet_broadcastSignedTransaction', signedTxData) +} + +export const getTransaction = async (txHash: string): Promise => { + return await callJsonRpc('chain_transaction', [txHash]) +} + +export const getMempoolInfo = async (): Promise => { + return await callJsonRpc('chain_mempool', []) +} diff --git a/src/api/request.ts b/src/api/request.ts new file mode 100644 index 0000000..4f82a9b --- /dev/null +++ b/src/api/request.ts @@ -0,0 +1,14 @@ +import request from '@/api/config' + +export const callJsonRpc = (method: string, params: any = []) => { + const requestData = { + jsonrpc: '2.0', + method, + params, + id: 1, + } + return request({ + method: 'POST', + data: requestData, + }) +} diff --git a/src/components/WalletInfo.vue b/src/components/WalletInfo.vue index b3c9a06..9b160bf 100644 --- a/src/components/WalletInfo.vue +++ b/src/components/WalletInfo.vue @@ -1,69 +1,155 @@ @@ -72,6 +158,18 @@ const handleSend = () => { @include card-base; } +.loading-state, +.empty-state { + text-align: center; + padding: var(--spacing-3xl); + color: var(--text-secondary); + + p { + margin: var(--spacing-lg) 0 0; + font-size: var(--font-base); + } +} + .balance-section { text-align: center; margin-bottom: var(--spacing-3xl); diff --git a/src/components/auth/CreateWalletComponent.vue b/src/components/auth/CreateWalletComponent.vue deleted file mode 100644 index 1254a8c..0000000 --- a/src/components/auth/CreateWalletComponent.vue +++ /dev/null @@ -1,639 +0,0 @@ - - - - diff --git a/src/components/common/SpinnerCommon.vue b/src/components/common/SpinnerCommon.vue new file mode 100644 index 0000000..cb7500b --- /dev/null +++ b/src/components/common/SpinnerCommon.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/src/components/index.ts b/src/components/index.ts index 059cbdb..7aa6125 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1,25 +1,7 @@ import LayoutVue from './common/LayoutVue.vue' import ButtonCommon from './common/ButtonCommon.vue' import FormCommon from './common/FormCommon.vue' -import OnboardingComponent from './auth/OnboardingComponent.vue' -import OpenWalletComponent from './auth/OpenWalletComponent.vue' -import CreateWalletComponent from './auth/CreateWalletComponent.vue' -import RecoverySeedComponent from './auth/RecoverySeedComponent.vue' -import ConfirmSeedComponent from './auth/ConfirmSeedComponent.vue' -import ImportWalletComponent from './auth/ImportWalletComponent.vue' -import KeystoreDownloadComponent from './auth/KeystoreDownloadComponent.vue' +import SpinnerCommon from './common/SpinnerCommon.vue' import { IconCommon } from './icon' -export { - LayoutVue, - ButtonCommon, - FormCommon, - OnboardingComponent, - OpenWalletComponent, - CreateWalletComponent, - RecoverySeedComponent, - ConfirmSeedComponent, - IconCommon, - ImportWalletComponent, - KeystoreDownloadComponent, -} +export { LayoutVue, ButtonCommon, FormCommon, SpinnerCommon, IconCommon } diff --git a/src/composables/useNeptuneWallet.ts b/src/composables/useNeptuneWallet.ts new file mode 100644 index 0000000..3aa5f29 --- /dev/null +++ b/src/composables/useNeptuneWallet.ts @@ -0,0 +1,292 @@ +import { computed } from 'vue' +import { useNeptuneStore } from '@/stores/neptuneStore' +import * as API from '@/api/neptuneApi' +import type { GenerateSeedResult, ViewKeyResult } from '@/interface' +import initWasm, { + generate_seed, + get_viewkey, + address_from_seed, + validate_seed_phrase, + decode_viewkey, +} from '@neptune/wasm' + +let wasmInitialized = false +let initPromise: Promise | null = null + +export function useNeptuneWallet() { + const store = useNeptuneStore() + + // ===== WASM METHODS ===== + const ensureWasmInitialized = async (): Promise => { + if (wasmInitialized) { + return + } + + if (initPromise) { + return initPromise + } + + initPromise = (async () => { + try { + store.setLoading(true) + store.setError(null) + + await initWasm() + + wasmInitialized = true + } catch (err) { + wasmInitialized = false + const errorMsg = 'Failed to initialize Neptune WASM' + store.setError(errorMsg) + console.error('WASM init error:', err) + throw new Error(errorMsg) + } finally { + store.setLoading(false) + } + })() + + return initPromise + } + + const generateWallet = async (): Promise => { + try { + store.setLoading(true) + store.setError(null) + + await ensureWasmInitialized() + + const resultJson = generate_seed() + const result: GenerateSeedResult = JSON.parse(resultJson) + + store.setSeedPhrase(result.seed_phrase) + store.setReceiverId(result.receiver_identifier) + + const viewKeyResult = await getViewKeyFromSeed(result.seed_phrase, store.getNetwork) + + store.setViewKey(viewKeyResult.view_key) + store.setAddress(viewKeyResult.address) + + return result + } catch (err) { + const errorMsg = err instanceof Error ? err.message : 'Failed to generate wallet' + store.setError(errorMsg) + throw err + } finally { + store.setLoading(false) + } + } + + const getViewKeyFromSeed = async ( + seedPhrase: string[], + network: 'mainnet' | 'testnet' + ): Promise => { + await ensureWasmInitialized() + const seedPhraseJson = JSON.stringify(seedPhrase) + const resultJson = get_viewkey(seedPhraseJson, network) + return JSON.parse(resultJson) + } + + const importWallet = async ( + seedPhrase: string[], + network: 'mainnet' | 'testnet' = 'testnet' + ): Promise => { + try { + store.setLoading(true) + store.setError(null) + + const isValid = await validateSeedPhrase(seedPhrase) + if (!isValid) { + throw new Error('Invalid seed phrase') + } + + const result = await getViewKeyFromSeed(seedPhrase, network) + + store.setSeedPhrase(seedPhrase) + store.setReceiverId(result.receiver_identifier) + store.setViewKey(result.view_key) + store.setAddress(result.address) + store.setNetwork(network) + + return result + } catch (err) { + const errorMsg = err instanceof Error ? err.message : 'Failed to import wallet' + store.setError(errorMsg) + throw err + } finally { + store.setLoading(false) + } + } + + const getAddressFromSeed = async ( + seedPhrase: string[], + network: 'mainnet' | 'testnet' + ): Promise => { + await ensureWasmInitialized() + const seedPhraseJson = JSON.stringify(seedPhrase) + return address_from_seed(seedPhraseJson, network) + } + + const validateSeedPhrase = async (seedPhrase: string[]): Promise => { + try { + await ensureWasmInitialized() + const seedPhraseJson = JSON.stringify(seedPhrase) + return validate_seed_phrase(seedPhraseJson) + } catch (err) { + console.error('Validation error:', err) + return false + } + } + + const decodeViewKey = async (viewKeyHex: string): Promise<{ receiver_identifier: string }> => { + await ensureWasmInitialized() + const resultJson = decode_viewkey(viewKeyHex) + return JSON.parse(resultJson) + } + + // ===== API METHODS ===== + + const getUtxos = async ( + startBlock: number = 0, + endBlock: number | null = null, + maxSearchDepth: number = 1000 + ): Promise => { + try { + if (!store.getViewKey) { + throw new Error('No view key available. Please import or generate a wallet first.') + } + + store.setLoading(true) + store.setError(null) + + const response = await API.getUtxosFromViewKey( + store.getViewKey, + startBlock, + endBlock, + maxSearchDepth + ) + + const result = response.data?.result || response.data + store.setUtxos(result.utxos || result || []) + return result + } catch (err) { + const errorMsg = err instanceof Error ? err.message : 'Failed to get UTXOs' + store.setError(errorMsg) + throw err + } finally { + store.setLoading(false) + } + } + + const getBalance = async (): Promise => { + try { + store.setLoading(true) + store.setError(null) + + const response = await API.getBalance() + const result = response.data?.result || response.data + store.setBalance(result.balance || result) + + return result + } catch (err) { + const errorMsg = err instanceof Error ? err.message : 'Failed to get balance' + store.setError(errorMsg) + throw err + } finally { + store.setLoading(false) + } + } + + const getBlockHeight = async (): Promise => { + try { + store.setLoading(true) + store.setError(null) + + const response = await API.getBlockHeight() + const result = response.data?.result || response.data + return result.height || result + } catch (err) { + const errorMsg = err instanceof Error ? err.message : 'Failed to get block height' + store.setError(errorMsg) + throw err + } finally { + store.setLoading(false) + } + } + + const getNetworkInfo = async (): Promise => { + try { + store.setLoading(true) + store.setError(null) + + const response = await API.getNetworkInfo() + const result = response.data?.result || response.data + return result + } catch (err) { + const errorMsg = err instanceof Error ? err.message : 'Failed to get network info' + store.setError(errorMsg) + throw err + } finally { + store.setLoading(false) + } + } + + const sendTransaction = async ( + toAddress: string, + amount: string, + fee: string + ): Promise => { + try { + store.setLoading(true) + store.setError(null) + + const response = await API.sendTransaction(toAddress, amount, fee) + const result = response.data?.result || response.data + return result + } catch (err) { + const errorMsg = err instanceof Error ? err.message : 'Failed to send transaction' + store.setError(errorMsg) + throw err + } finally { + store.setLoading(false) + } + } + + const setNetwork = async (network: 'mainnet' | 'testnet') => { + store.setNetwork(network) + + if (store.getSeedPhrase) { + const viewKeyResult = await getViewKeyFromSeed(store.getSeedPhrase, network) + store.setAddress(viewKeyResult.address) + store.setViewKey(viewKeyResult.view_key) + } + } + + // ===== UTILITY METHODS ===== + const clearWallet = () => { + store.clearWallet() + } + + return { + walletState: computed(() => store.getWallet), + isLoading: computed(() => store.getLoading), + error: computed(() => store.getError), + hasWallet: computed(() => store.hasWallet), + + initWasm: ensureWasmInitialized, + generateWallet, + importWallet, + getViewKeyFromSeed, + getAddressFromSeed, + validateSeedPhrase, + decodeViewKey, + + getUtxos, + getBalance, + getBlockHeight, + getNetworkInfo, + sendTransaction, + + clearWallet, + setNetwork, + } +} diff --git a/src/interface/index.ts b/src/interface/index.ts index e2116e3..991fe7c 100644 --- a/src/interface/index.ts +++ b/src/interface/index.ts @@ -1,2 +1,3 @@ export * from './common' export * from './home' +export * from './neptune' diff --git a/src/interface/neptune.ts b/src/interface/neptune.ts new file mode 100644 index 0000000..49268f8 --- /dev/null +++ b/src/interface/neptune.ts @@ -0,0 +1,20 @@ +export interface WalletState { + seedPhrase: string[] | null + receiverId: string | null + viewKey: string | null + address: string | null + network: 'mainnet' | 'testnet' + balance: string | null + utxos: any[] +} + +export interface GenerateSeedResult { + seed_phrase: string[] + receiver_identifier: string +} + +export interface ViewKeyResult { + receiver_identifier: string + view_key: string + address: string +} diff --git a/src/router/route.ts b/src/router/route.ts index ce7922c..a365d6f 100644 --- a/src/router/route.ts +++ b/src/router/route.ts @@ -1,45 +1,26 @@ import * as Page from '@/views' -import { getToken } from '@/utils' +import { useNeptuneStore } from '@/stores/neptuneStore' -const ifAuthenticated = (to: any, from: any, next: any) => { - if (getToken()) { +export const ifAuthenticated = (to: any, from: any, next: any) => { + const neptuneStore = useNeptuneStore() + if (neptuneStore.getReceiverId) { next() return } next('/login') } -const ifNotAuthenticated = (to: any, from: any, next: any) => { - if (!getToken()) { - next() - return - } - next('/') -} - export const routes: any = [ { path: '/', - name: 'index', + name: 'home', component: Page.Home, + beforeEnter: ifAuthenticated, }, { path: '/login', name: 'login', component: Page.Auth, - beforeEnter: ifNotAuthenticated, - }, - { - path: '/recovery-seed', - name: 'recovery-seed', - component: Page.Auth, - beforeEnter: ifNotAuthenticated, - }, - { - path: '/confirm-seed', - name: 'confirm-seed', - component: Page.Auth, - beforeEnter: ifNotAuthenticated, }, { path: '/:pathMatch(.*)*', diff --git a/src/stores/index.ts b/src/stores/index.ts index 0282611..4782b3d 100644 --- a/src/stores/index.ts +++ b/src/stores/index.ts @@ -1,2 +1,3 @@ export * from './seedStore' export * from './authStore' +export * from './neptuneStore' diff --git a/src/stores/neptuneStore.ts b/src/stores/neptuneStore.ts new file mode 100644 index 0000000..ef8550c --- /dev/null +++ b/src/stores/neptuneStore.ts @@ -0,0 +1,113 @@ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' +import type { WalletState } from '@/interface' + +export const useNeptuneStore = defineStore('neptune', () => { + // ===== STATE ===== + const wallet = ref({ + seedPhrase: null, + receiverId: null, + viewKey: null, + address: null, + network: 'testnet', + balance: null, + utxos: [], + }) + + const isLoading = ref(false) + const error = ref(null) + + // ===== SETTERS ===== + + const setSeedPhrase = (seedPhrase: string[] | null) => { + wallet.value.seedPhrase = seedPhrase + } + + const setReceiverId = (receiverId: string | null) => { + wallet.value.receiverId = receiverId + } + + const setViewKey = (viewKey: string | null) => { + wallet.value.viewKey = viewKey + } + + const setAddress = (address: string | null) => { + wallet.value.address = address + } + + const setNetwork = (network: 'mainnet' | 'testnet') => { + wallet.value.network = network + } + + const setBalance = (balance: string | null) => { + wallet.value.balance = balance + } + + const setUtxos = (utxos: any[]) => { + wallet.value.utxos = utxos + } + + const setLoading = (loading: boolean) => { + isLoading.value = loading + } + + const setError = (err: string | null) => { + error.value = err + } + + const setWallet = (walletData: Partial) => { + wallet.value = { ...wallet.value, ...walletData } + } + + const clearWallet = () => { + wallet.value = { + seedPhrase: null, + receiverId: null, + viewKey: null, + address: null, + network: 'testnet', + balance: null, + utxos: [], + } + error.value = null + } + + // ===== GETTERS ===== + const getWallet = computed(() => wallet.value) + const getSeedPhrase = computed(() => wallet.value.seedPhrase) + const getReceiverId = computed(() => wallet.value.receiverId) + const getViewKey = computed(() => wallet.value.viewKey) + const getAddress = computed(() => wallet.value.address) + const getNetwork = computed(() => wallet.value.network) + const getBalance = computed(() => wallet.value.balance) + const getUtxos = computed(() => wallet.value.utxos) + const hasWallet = computed(() => wallet.value.address !== null) + const getLoading = computed(() => isLoading.value) + const getError = computed(() => error.value) + + return { + getWallet, + getSeedPhrase, + getReceiverId, + getViewKey, + getAddress, + getNetwork, + getBalance, + getUtxos, + hasWallet, + getLoading, + getError, + + setSeedPhrase, + setReceiverId, + setViewKey, + setAddress, + setNetwork, + setBalance, + setUtxos, + setLoading, + setError, + setWallet, + clearWallet, + } +}) diff --git a/src/types/neptune-wasm.d.ts b/src/types/neptune-wasm.d.ts new file mode 100644 index 0000000..5901231 --- /dev/null +++ b/src/types/neptune-wasm.d.ts @@ -0,0 +1,39 @@ +/** + * Type declarations for Neptune WASM module + */ + +declare module '/wasm/neptune_wasm.js' { + export default function init(): Promise + + export function generate_seed(): string + export function get_viewkey(seedPhraseJson: string, network: string): string + export function address_from_seed(seedPhraseJson: string, network: string): string + export function validate_seed_phrase(seedPhraseJson: string): boolean + export function decode_viewkey(viewKeyHex: string): string + export function get_balance(rpcUrl: string): Promise + export function get_block_height(rpcUrl: string): Promise + export function get_network_info(rpcUrl: string): Promise + export function get_wallet_address(rpcUrl: string): Promise + export function get_utxos_from_viewkey( + rpcUrl: string, + viewKeyHex: string, + startBlock: number, + endBlock: number | null + ): Promise + export function build_and_sign_tx(requestJson: string): string + export function send_tx_jsonrpc( + rpcUrl: string, + toAddress: string, + amount: string, + fee: string + ): Promise +} + +// Global type definitions +declare global { + interface Window { + neptuneWasm?: any + } +} + +export {} diff --git a/src/views/Auth/AuthView.vue b/src/views/Auth/AuthView.vue index 486e8f5..ae517b1 100644 --- a/src/views/Auth/AuthView.vue +++ b/src/views/Auth/AuthView.vue @@ -1,6 +1,6 @@ @@ -224,6 +144,9 @@ onMounted(() => { Make sure you wrote the phrase down correctly by answering this quick checkup.

+

+ Question {{ correctCount + 1 }} / {{ totalQuestions }} +

@@ -250,14 +173,42 @@ onMounted(() => {
-

✓ Correct! You can proceed.

+

+ ✓ Correct! You answered all {{ totalQuestions }} questions correctly. +

+

+ ✓ Correct! Next question... +

✗ Incorrect. Please try again.

+ BACK + + + NEXT QUESTION + + { line-height: var(--leading-normal); margin: 0; } + + .progress-text { + margin-top: var(--spacing-md); + font-weight: var(--font-bold); + color: var(--primary-color); + font-size: var(--font-base); + } } .quiz-section { @@ -411,10 +369,14 @@ onMounted(() => { display: flex; justify-content: space-between; gap: var(--spacing-md); + + &:has(:only-child) { + justify-content: flex-end; + } } } -@media (max-width: 640px) { +@include screen(mobile) { .confirm-container { padding: var(--spacing-md); } @@ -423,14 +385,16 @@ onMounted(() => { max-width: 100%; } - .quiz-section { - .answer-options { - grid-template-columns: 1fr; + .confirm-content { + .quiz-section { + .answer-options { + grid-template-columns: 1fr; + } } - } - .confirm-actions { - flex-direction: column; + .confirm-actions { + flex-direction: column; + } } } diff --git a/src/views/Auth/components/ConfirmTab.vue b/src/views/Auth/components/ConfirmTab.vue index 1a03eff..ee7db97 100644 --- a/src/views/Auth/components/ConfirmTab.vue +++ b/src/views/Auth/components/ConfirmTab.vue @@ -1,5 +1,5 @@ + + + diff --git a/src/components/auth/ImportWalletComponent.vue b/src/views/Auth/components/ImportWalletComponent.vue similarity index 99% rename from src/components/auth/ImportWalletComponent.vue rename to src/views/Auth/components/ImportWalletComponent.vue index b9f3e8f..aa33cfa 100644 --- a/src/components/auth/ImportWalletComponent.vue +++ b/src/views/Auth/components/ImportWalletComponent.vue @@ -255,7 +255,7 @@ const handleContinue = () => { margin-top: 3px; } } -@media (max-width: 600px) { +@include screen(mobile) { .import-wallet { padding: 16px 5px; } diff --git a/src/components/auth/KeystoreDownloadComponent.vue b/src/views/Auth/components/KeystoreDownloadComponent.vue similarity index 98% rename from src/components/auth/KeystoreDownloadComponent.vue rename to src/views/Auth/components/KeystoreDownloadComponent.vue index bc3cb46..51b85f2 100644 --- a/src/components/auth/KeystoreDownloadComponent.vue +++ b/src/views/Auth/components/KeystoreDownloadComponent.vue @@ -218,13 +218,19 @@ function handleBack() { margin-right: 8px; } } -@media (max-width: 900px) { +@include screen(tablet) { .steps-bar .step { min-width: 90px; font-size: 14px; } } -@media (max-width: 650px) { + +@include screen(mobile) { + .steps-bar .step { + min-width: 90px; + font-size: 14px; + } + .keystore-step { padding: 15px 3px 13px 3px; } diff --git a/src/views/Auth/components/LoginTab.vue b/src/views/Auth/components/LoginTab.vue index 1645a10..25a0a95 100644 --- a/src/views/Auth/components/LoginTab.vue +++ b/src/views/Auth/components/LoginTab.vue @@ -1,6 +1,6 @@