From 69d55cc9da6ed75908b344c9b5517a1f934f42c9 Mon Sep 17 00:00:00 2001 From: "smx.pusha" <139338836+smxpusha@users.noreply.github.com> Date: Tue, 18 Aug 2026 10:49:36 +0200 Subject: [PATCH] ENH - rebuild VaultX profile management Move profile identity edits into a secure sheet, add header sign out, persist preferences immediately, and support authenticated password rotation with the existing scrypt provider. --- frontend/src/stores/crypto.test.ts | 6 +- frontend/src/stores/crypto.ts | 3 +- frontend/src/stores/phone.ts | 14 +- .../src/views/apps/CryptoApp.contract.test.ts | 12 + frontend/src/views/apps/CryptoApp.vue | 259 +++++++++++++----- sky_phone/config/locales/de.lua | 2 +- sky_phone/config/locales/en.lua | 2 +- sky_phone/source/server/crypto.lua | 60 +++- 8 files changed, 265 insertions(+), 93 deletions(-) diff --git a/frontend/src/stores/crypto.test.ts b/frontend/src/stores/crypto.test.ts index f2b57ea..6e38581 100644 --- a/frontend/src/stores/crypto.test.ts +++ b/frontend/src/stores/crypto.test.ts @@ -130,7 +130,8 @@ describe('crypto store', () => { await crypto.updateProfile({ handle: 'skyline', hideBalances: true, - password: '', + currentPassword: '', + newPassword: '', priceAlerts: false, tradeConfirmations: true, }), @@ -138,7 +139,8 @@ describe('crypto store', () => { expect(mockNuiCall).toHaveBeenCalledWith('crypto:update-profile', { handle: 'skyline', hideBalances: true, - password: '', + currentPassword: '', + newPassword: '', priceAlerts: false, tradeConfirmations: true, }) diff --git a/frontend/src/stores/crypto.ts b/frontend/src/stores/crypto.ts index ed834a1..58a2488 100644 --- a/frontend/src/stores/crypto.ts +++ b/frontend/src/stores/crypto.ts @@ -113,7 +113,8 @@ export const useCryptoStore = defineStore('crypto', { async updateProfile(payload: { handle: string hideBalances: boolean - password: string + currentPassword: string + newPassword: string priceAlerts: boolean tradeConfirmations: boolean }): Promise { diff --git a/frontend/src/stores/phone.ts b/frontend/src/stores/phone.ts index 716d4de..7768f4d 100644 --- a/frontend/src/stores/phone.ts +++ b/frontend/src/stores/phone.ts @@ -535,10 +535,20 @@ const cryptoFallbackLocales = { hideBalances: 'Privacy mode', hideBalancesBody: 'Hide balances across VaultX.', identity: 'Profile identity', - passwordForChange: 'Password for handle change', - passwordOptional: 'Only required when changing handle', + edit: 'Edit', + account: 'Account & security', + editTitle: 'Edit profile', + editBody: + 'Update your handle or set a new password. Your current password confirms identity changes.', + currentPassword: 'Current password', + currentPasswordPlaceholder: 'Required for handle or password changes', + newPassword: 'New password', + newPasswordPlaceholder: 'Leave blank to keep your current password', + passwordSecurity: + 'Passwords use memory-hard hashing and are never shown again.', saved: 'Profile saved securely.', save: 'Save profile', + saveChanges: 'Save changes', securityTitle: 'Protected profile', securityBody: 'Your profile stays bound to this framework character and financial session.', diff --git a/frontend/src/views/apps/CryptoApp.contract.test.ts b/frontend/src/views/apps/CryptoApp.contract.test.ts index 5f1f18e..7dd10f5 100644 --- a/frontend/src/views/apps/CryptoApp.contract.test.ts +++ b/frontend/src/views/apps/CryptoApp.contract.test.ts @@ -58,6 +58,18 @@ describe('VaultX crypto app contracts', () => { expect(server).toContain('`price_alerts`') }) + it('supports secure profile editing and header sign out', () => { + expect(source).toContain('class="profile-signout"') + expect(source).toContain('class="profile-edit-button"') + expect(source).toContain("sheet.value = 'profile'") + expect(source).toContain('v-model="profileCurrentPassword"') + expect(source).toContain('v-model="profileNewPassword"') + expect(server).toContain( + 'verify_password(profile.id, data.currentPassword)', + ) + expect(server).toContain('CryptoHashPassword(new_password)') + }) + it('uses the premium dashboard hierarchy without manual refresh controls', () => { expect(source).toContain('class="portfolio-shell"') expect(source).toContain('class="featured-market"') diff --git a/frontend/src/views/apps/CryptoApp.vue b/frontend/src/views/apps/CryptoApp.vue index 64d6387..c3bfd76 100644 --- a/frontend/src/views/apps/CryptoApp.vue +++ b/frontend/src/views/apps/CryptoApp.vue @@ -44,7 +44,7 @@ import { } from '@/ui' type Tab = 'portfolio' | 'markets' | 'activity' | 'profile' -type Sheet = 'trade' | 'deposit' | 'withdraw' | null +type Sheet = 'trade' | 'deposit' | 'withdraw' | 'profile' | null const crypto = useCryptoStore() const phone = usePhoneStore() const tab = ref('portfolio') @@ -61,7 +61,8 @@ const financialPassword = ref('') const showPassword = ref(false) const formError = ref('') const profileHandle = ref('') -const profilePassword = ref('') +const profileCurrentPassword = ref('') +const profileNewPassword = ref('') const priceAlerts = ref(true) const confirmations = ref(true) const hideBalances = ref(false) @@ -285,9 +286,19 @@ function openSettlement(next: 'deposit' | 'withdraw') { financialPassword.value = '' formError.value = '' } +function openProfileEditor() { + profileHandle.value = profile.value?.handle ?? '' + profileCurrentPassword.value = '' + profileNewPassword.value = '' + formError.value = '' + saved.value = false + sheet.value = 'profile' +} function closeSheet() { sheet.value = null crypto.pendingQuote = null + profileCurrentPassword.value = '' + profileNewPassword.value = '' } async function submitAuth() { @@ -332,16 +343,36 @@ async function saveProfile() { const success = await crypto.updateProfile({ handle: profileHandle.value.trim(), hideBalances: hideBalances.value, - password: profilePassword.value, + currentPassword: profileCurrentPassword.value, + newPassword: profileNewPassword.value, priceAlerts: priceAlerts.value, tradeConfirmations: confirmations.value, }) if (!success) formError.value = errorText(crypto.error) else { - profilePassword.value = '' + closeSheet() saved.value = true } } +async function savePreferences() { + saved.value = false + formError.value = '' + const success = await crypto.updateProfile({ + handle: profile.value?.handle ?? '', + hideBalances: hideBalances.value, + currentPassword: '', + newPassword: '', + priceAlerts: priceAlerts.value, + tradeConfirmations: confirmations.value, + }) + if (!success) formError.value = errorText(crypto.error) + else saved.value = true +} +async function signOut() { + closeSheet() + await crypto.logout() + tab.value = 'portfolio' +} watch( profile, @@ -394,6 +425,18 @@ onMounted(() => void crypto.load()) {{ t(`tabs.${tab}`) }} + void crypto.load()) }} {{ t('profile.verified') }} -

@{{ profile?.handle }}

-

{{ t('profile.verified') }}

+
+ +

@{{ profile?.handle }}

+

{{ t('profile.verified') }}

+
+ + {{ t('profile.edit') }} + +
VAULTX ID {{ profile?.id.slice(0, 8).toUpperCase() }} @@ -1037,52 +1092,32 @@ onMounted(() => void crypto.load()) >{{ t('profile.priceAlerts') }}{{ t('profile.priceAlertsBody') }} -

{{ t('profile.identity') }}

-
- -

- {{ t('profile.saved') }} -

-

{{ formError }}

- {{ t('profile.save') }} -
- {{ t('profile.securityTitle') }}{{ t('profile.securityBody') }}{{ t('logout') }} +

+ {{ t('profile.saved') }} +

+

{{ formError }}

@@ -1140,19 +1175,24 @@ onMounted(() => void crypto.load()) :market="selectedMarket" /> - + + {{ sheet === 'trade' && selectedMarket ? selectedMarket.name - : t('activity.cash') + : sheet === 'profile' + ? t('profile.account') + : t('activity.cash') }}

{{ sheet === 'trade' ? `${t(`trade.${side}`)} ${selectedMarket?.symbol}` - : t(`actions.${sheet}`) + : sheet === 'profile' + ? t('profile.editTitle') + : t(`actions.${sheet}`) }}

@@ -1222,7 +1262,43 @@ onMounted(() => void crypto.load()) t(crypto.pendingQuote ? 'trade.confirm' : 'trade.getQuote') }}