mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-08-29 01:08:59 +00:00
ENH - share VaultX receiving keys
Add explicit Copy and Share actions to the profile key card. Use the CEF-compatible clipboard helper for reliable copying and route sharing through the phone-owned EasyShare flow with localized payload text.
This commit is contained in:
@@ -110,6 +110,18 @@ describe('VaultX crypto app contracts', () => {
|
||||
expect(transferServer).not.toContain('Bridge.Framework.RemoveMoney')
|
||||
})
|
||||
|
||||
it('copies and shares the public account key through phone-owned flows', () => {
|
||||
expect(source).toContain("import { copyText } from '@/utils/clipboard'")
|
||||
expect(source).toContain(
|
||||
"import { useEasyShareStore } from '@/stores/easyshare'",
|
||||
)
|
||||
expect(source).toContain('class="wallet-key-card__actions"')
|
||||
expect(source).toContain('@click="copyWalletKey"')
|
||||
expect(source).toContain('@click="shareWalletKey"')
|
||||
expect(source).toContain("appId: 'crypto'")
|
||||
expect(source).toContain("kind: 'text'")
|
||||
})
|
||||
|
||||
it('uses the premium dashboard hierarchy without manual refresh controls', () => {
|
||||
expect(source).toContain('class="portfolio-shell"')
|
||||
expect(source).toContain('class="featured-market"')
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
LogOut,
|
||||
Settings2,
|
||||
Send,
|
||||
Share2,
|
||||
ShieldCheck,
|
||||
Sparkles,
|
||||
UserRound,
|
||||
@@ -27,6 +28,7 @@ import { computed, onMounted, ref, watch } from 'vue'
|
||||
import cryptoHeaderLogo from '@/assets/img/app-icons/crypto-header-logo.png'
|
||||
import CryptoLogo from '@/components/crypto/CryptoLogo.vue'
|
||||
import { useCryptoStore } from '@/stores/crypto'
|
||||
import { useEasyShareStore } from '@/stores/easyshare'
|
||||
import { usePhoneStore } from '@/stores/phone'
|
||||
import type {
|
||||
CryptoActivity,
|
||||
@@ -52,6 +54,7 @@ import {
|
||||
SkyStatusCard,
|
||||
SkyToggle,
|
||||
} from '@/ui'
|
||||
import { copyText } from '@/utils/clipboard'
|
||||
|
||||
type Tab = 'portfolio' | 'markets' | 'activity' | 'profile'
|
||||
type Sheet = 'trade' | 'deposit' | 'withdraw' | 'profile' | 'send' | null
|
||||
@@ -81,6 +84,7 @@ const CHART_PERIOD_CONFIG: Record<
|
||||
},
|
||||
}
|
||||
const crypto = useCryptoStore()
|
||||
const easyShare = useEasyShareStore()
|
||||
const phone = usePhoneStore()
|
||||
const tab = ref<Tab>('portfolio')
|
||||
const authMode = ref<'login' | 'register'>('login')
|
||||
@@ -485,8 +489,8 @@ async function submitAuth() {
|
||||
passwordStrength.value < 4
|
||||
? 'errors.invalid_password'
|
||||
: password.value !== confirmPassword.value
|
||||
? 'errors.password_mismatch'
|
||||
: 'errors.accept_terms',
|
||||
? 'errors.password_mismatch'
|
||||
: 'errors.accept_terms',
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -530,16 +534,24 @@ async function submitTransfer() {
|
||||
if (!success) formError.value = errorText(crypto.error)
|
||||
else closeSheet()
|
||||
}
|
||||
async function copyWalletKey() {
|
||||
function copyWalletKey() {
|
||||
if (!profile.value?.walletKey) return
|
||||
try {
|
||||
await navigator.clipboard.writeText(profile.value.walletKey)
|
||||
walletKeyCopied.value = true
|
||||
walletKeyCopied.value = copyText(profile.value.walletKey)
|
||||
if (walletKeyCopied.value) {
|
||||
globalThis.setTimeout(() => (walletKeyCopied.value = false), 1800)
|
||||
} catch {
|
||||
walletKeyCopied.value = false
|
||||
}
|
||||
}
|
||||
function shareWalletKey() {
|
||||
if (!profile.value?.walletKey) return
|
||||
easyShare.open({
|
||||
appId: 'crypto',
|
||||
copyText: `${t('profile.shareText')}\n${profile.value.walletKey}`,
|
||||
id: profile.value.walletKey,
|
||||
kind: 'text',
|
||||
subtitle: `@${profile.value.handle}`,
|
||||
title: t('profile.shareTitle'),
|
||||
})
|
||||
}
|
||||
function activityValue(item: CryptoActivity) {
|
||||
if (item.type === 'transfer_in' || item.type === 'transfer_out') {
|
||||
return `${quantity(item.quantity ?? '0')} ${market(item.marketId)?.symbol ?? ''}`
|
||||
@@ -1425,16 +1437,24 @@ onMounted(() => void crypto.load())
|
||||
</div>
|
||||
</section>
|
||||
<SkyCard class="wallet-key-card" :content-wrap="false">
|
||||
<span class="wallet-key-card__icon"><KeyRound :size="20" /></span>
|
||||
<span>
|
||||
<small>{{ t('profile.walletKey') }}</small>
|
||||
<b>{{ profile?.walletKey }}</b>
|
||||
<em>{{ t('profile.walletKeyBody') }}</em>
|
||||
</span>
|
||||
<button :aria-label="t('profile.copyKey')" @click="copyWalletKey">
|
||||
<CheckCircle2 v-if="walletKeyCopied" :size="17" />
|
||||
<Copy v-else :size="17" />
|
||||
</button>
|
||||
<div class="wallet-key-card__content">
|
||||
<span class="wallet-key-card__icon"><KeyRound :size="20" /></span>
|
||||
<span>
|
||||
<small>{{ t('profile.walletKey') }}</small>
|
||||
<b>{{ profile?.walletKey }}</b>
|
||||
<em>{{ t('profile.walletKeyBody') }}</em>
|
||||
</span>
|
||||
</div>
|
||||
<div class="wallet-key-card__actions">
|
||||
<SkyButton variant="secondary" tonal @click="copyWalletKey">
|
||||
<CheckCircle2 v-if="walletKeyCopied" :size="16" />
|
||||
<Copy v-else :size="16" />
|
||||
{{ t(walletKeyCopied ? 'profile.copied' : 'profile.copyKey') }}
|
||||
</SkyButton>
|
||||
<SkyButton @click="shareWalletKey">
|
||||
<Share2 :size="16" />{{ t('profile.shareKey') }}
|
||||
</SkyButton>
|
||||
</div>
|
||||
</SkyCard>
|
||||
<div class="profile-stats profile-stats--premium">
|
||||
<div>
|
||||
@@ -3651,9 +3671,7 @@ onMounted(() => void crypto.load())
|
||||
}
|
||||
.wallet-key-card {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||
gap: 11px;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-top: 11px;
|
||||
padding: 13px;
|
||||
background:
|
||||
@@ -3666,6 +3684,12 @@ onMounted(() => void crypto.load())
|
||||
border: 1px solid rgba(101, 251, 210, 0.13);
|
||||
border-radius: 19px;
|
||||
}
|
||||
.wallet-key-card__content {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
gap: 11px;
|
||||
align-items: center;
|
||||
}
|
||||
.wallet-key-card__icon {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
@@ -3676,7 +3700,7 @@ onMounted(() => void crypto.load())
|
||||
border: 1px solid rgba(101, 251, 210, 0.13);
|
||||
border-radius: 14px;
|
||||
}
|
||||
.wallet-key-card > span:nth-child(2) {
|
||||
.wallet-key-card__content > span:nth-child(2) {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
gap: 2px;
|
||||
@@ -3695,15 +3719,18 @@ onMounted(() => void crypto.load())
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.wallet-key-card button {
|
||||
.wallet-key-card__actions {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
color: var(--vault-mint);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.07);
|
||||
border-radius: 12px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 7px;
|
||||
}
|
||||
.wallet-key-card__actions :deep(.sky-button) {
|
||||
min-height: 42px;
|
||||
gap: 6px;
|
||||
padding: 0 10px;
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
border-radius: 13px;
|
||||
}
|
||||
.profile-stats--premium {
|
||||
margin-top: 10px;
|
||||
|
||||
@@ -1064,7 +1064,7 @@ Locales["de"] = {
|
||||
settlement = { depositBody = "Übertrage Bankguthaben in dein verbuchtes VaultX-Guthaben.", withdrawBody = "Zahle verbuchtes VaultX-Guthaben auf das Bankkonto deines Charakters aus.", amount = "Ganzer Bankbetrag", confirm = "Sicher bestätigen" },
|
||||
transfer = { title = "Krypto senden", subtitle = "VaultX-Übertragung", keyTitle = "Öffentlicher Empfangsschlüssel", keyBody = "Prüfe den Empfänger, bevor du ein Asset auswählst. Bargeld kann hier niemals gesendet werden.", walletKey = "Crypto-Key des Empfängers", verify = "Prüfen", verifiedRecipient = "Verifizierter VaultX-Empfänger", asset = "Asset auswählen", quantity = "Krypto-Menge", available = "Verfügbarer Bestand", password = "Mit Passwort bestätigen", protected = "Atomare Wallet-Übertragung", protectedBody = "VaultX prüft Empfänger, Besitz, Menge und Bestand erneut auf dem Server und verbucht beide Wallets gemeinsam.", confirm = "Krypto sicher senden" },
|
||||
activity = { title = "Finanzaktivität", empty = "Keine Aktivität", emptyBody = "Einzahlungen, Auszahlungen, Handel und Krypto-Übertragungen erscheinen hier.", volume = "Handelsvolumen gesamt", completedTrades = "abgeschlossene Trades", cash = "Guthaben", filters = { all = "Alle", trades = "Trades", wallet = "Wallet" } },
|
||||
profile = { verified = "Charakterbesitz verifiziert", walletKey = "Öffentlicher Crypto-Key", walletKeyBody = "Teile diesen Key, um Krypto zu empfangen. Er gewährt keinen Kontozugriff.", copyKey = "Crypto-Key kopieren", trades = "Trades", volume = "Volumen", memberSince = "Mitglied seit", preferences = "Einstellungen", priceAlerts = "Kurswarnungen", priceAlertsBody = "Bei auffälligen Marktbewegungen benachrichtigen.", confirmations = "Handelsbestätigung", confirmationsBody = "Zusätzliche Bestätigung vor der Ausführung behalten.", hideBalances = "Privatsphäre-Modus", hideBalancesBody = "Beträge in VaultX verbergen.", identity = "Profilidentität", edit = "Bearbeiten", account = "Konto & Sicherheit", editTitle = "Profil bearbeiten", editBody = "Ändere deinen Benutzernamen oder lege ein neues Passwort fest. Dein aktuelles Passwort bestätigt Identitätsänderungen.", currentPassword = "Aktuelles Passwort", currentPasswordPlaceholder = "Für Namens- oder Passwortänderungen erforderlich", newPassword = "Neues Passwort", newPasswordPlaceholder = "Leer lassen, um das aktuelle Passwort zu behalten", passwordSecurity = "Passwörter werden speicherintensiv gehasht und nie wieder angezeigt.", saved = "Profil sicher gespeichert.", save = "Profil speichern", saveChanges = "Änderungen speichern", securityTitle = "Geschütztes Profil", securityBody = "Dein Profil bleibt an diesen Framework-Charakter und die Finanzsitzung gebunden." },
|
||||
profile = { verified = "Charakterbesitz verifiziert", walletKey = "Öffentlicher Crypto-Key", walletKeyBody = "Teile diesen Key, um Krypto zu empfangen. Er gewährt keinen Kontozugriff.", copyKey = "Kopieren", copied = "Kopiert", shareKey = "Teilen", shareTitle = "Mein VaultX Crypto-Key", shareText = "Sende Krypto an meinen öffentlichen VaultX-Key:", trades = "Trades", volume = "Volumen", memberSince = "Mitglied seit", preferences = "Einstellungen", priceAlerts = "Kurswarnungen", priceAlertsBody = "Bei auffälligen Marktbewegungen benachrichtigen.", confirmations = "Handelsbestätigung", confirmationsBody = "Zusätzliche Bestätigung vor der Ausführung behalten.", hideBalances = "Privatsphäre-Modus", hideBalancesBody = "Beträge in VaultX verbergen.", identity = "Profilidentität", edit = "Bearbeiten", account = "Konto & Sicherheit", editTitle = "Profil bearbeiten", editBody = "Ändere deinen Benutzernamen oder lege ein neues Passwort fest. Dein aktuelles Passwort bestätigt Identitätsänderungen.", currentPassword = "Aktuelles Passwort", currentPasswordPlaceholder = "Für Namens- oder Passwortänderungen erforderlich", newPassword = "Neues Passwort", newPasswordPlaceholder = "Leer lassen, um das aktuelle Passwort zu behalten", passwordSecurity = "Passwörter werden speicherintensiv gehasht und nie wieder angezeigt.", saved = "Profil sicher gespeichert.", save = "Profil speichern", saveChanges = "Änderungen speichern", securityTitle = "Geschütztes Profil", securityBody = "Dein Profil bleibt an diesen Framework-Charakter und die Finanzsitzung gebunden." },
|
||||
activityTypes = { buy = "Asset-Kauf", sell = "Asset-Verkauf", deposit = "Bankeinzahlung", withdrawal = "Bankauszahlung", transfer_in = "Krypto empfangen", transfer_out = "Krypto gesendet" },
|
||||
statuses = { completed = "Abgeschlossen", pending = "Prüfung ausstehend", failed = "Fehlgeschlagen", manual_review = "Manuelle Prüfung" },
|
||||
errors = { invalid_profile = "Prüfe deine VaultX-Profileinstellungen.", invalid_handle = "Nutze 3–20 Buchstaben, Zahlen, Punkte oder Unterstriche.", handle_taken = "Dieser VaultX-Benutzername ist bereits vergeben.", profile_exists = "Dieser Charakter besitzt bereits ein VaultX-Profil.", invalid_password = "Das Passwort muss 8–72 Zeichen lang sein.", password_mismatch = "Die Passwörter stimmen nicht überein.", accept_terms = "Bestätige, dass dies ein fiktives Ingame-Wallet ist.", invalid_credentials = "Das Passwort ist falsch.", locked = "Zu viele Versuche. Versuche es später erneut.", not_authenticated = "Logge dich zuerst bei VaultX ein.", invalid_amount = "Gib einen positiven ganzen Betrag ein.", invalid_quantity = "Gib eine Menge mit bis zu sechs Nachkommastellen ein.", invalid_wallet_key = "Gib einen vollständigen VX-Empfangsschlüssel ein.", recipient_not_found = "Zu diesem Key wurde kein aktives VaultX-Konto gefunden.", self_transfer = "Du kannst keine Krypto an deinen eigenen Key senden.", invalid_transfer = "Prüfe Empfänger, Asset und Krypto-Menge.", recipient_limit_exceeded = "Das Empfänger-Wallet kann diese Menge nicht aufnehmen.", insufficient_funds = "Dein verfügbarer Bestand reicht nicht aus.", insufficient_liquidity = "Die Börse kann diese Menge derzeit nicht ausführen.", quote_expired = "Das Angebot ist abgelaufen. Fordere ein neues an.", quote_unavailable = "Das Angebot ist nicht mehr verfügbar.", market_unavailable = "Dieser Markt ist vorübergehend pausiert.", limit_exceeded = "Die Anfrage überschreitet ein Börsenlimit.", duplicate_request = "Diese Anfrage wurde bereits verarbeitet.", rate_limited = "Zu viele Anfragen. Versuche es gleich erneut.", settlement_pending = "Eine Geldbewegung wartet bereits auf Prüfung.", service_unavailable = "VaultX ist vorübergehend nicht verfügbar.", request_failed = "Die sichere Börsenanfrage ist fehlgeschlagen.", default = "VaultX konnte die Anfrage nicht abschließen." },
|
||||
|
||||
@@ -1064,7 +1064,7 @@ Locales["en"] = {
|
||||
settlement = { depositBody = "Move bank funds into your settled VaultX cash balance.", withdrawBody = "Return settled VaultX cash to your character's bank account.", amount = "Whole bank amount", confirm = "Confirm securely" },
|
||||
transfer = { title = "Send crypto", subtitle = "VaultX transfer", keyTitle = "Public receiving key", keyBody = "Verify the recipient before selecting an asset. Cash can never be sent here.", walletKey = "Recipient crypto key", verify = "Verify", verifiedRecipient = "Verified VaultX recipient", asset = "Choose asset", quantity = "Crypto amount", available = "Available balance", password = "Confirm with password", protected = "Atomic wallet transfer", protectedBody = "VaultX rechecks recipient, ownership, quantity and balance on the server, then books both wallets together.", confirm = "Send crypto securely" },
|
||||
activity = { title = "Financial activity", empty = "No activity", emptyBody = "Deposits, withdrawals, trades and crypto transfers will appear here.", volume = "Lifetime trading volume", completedTrades = "completed trades", cash = "Cash", filters = { all = "All", trades = "Trades", wallet = "Wallet" } },
|
||||
profile = { verified = "Character ownership verified", walletKey = "Public crypto key", walletKeyBody = "Share this key to receive crypto. It cannot access your account.", copyKey = "Copy crypto key", trades = "Trades", volume = "Volume", memberSince = "Member since", preferences = "Preferences", priceAlerts = "Price alerts", priceAlertsBody = "Notify me about notable market moves.", confirmations = "Trade confirmations", confirmationsBody = "Keep an extra confirmation before execution.", hideBalances = "Privacy mode", hideBalancesBody = "Hide balances across VaultX.", identity = "Profile identity", 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." },
|
||||
profile = { verified = "Character ownership verified", walletKey = "Public crypto key", walletKeyBody = "Share this key to receive crypto. It cannot access your account.", copyKey = "Copy", copied = "Copied", shareKey = "Share", shareTitle = "My VaultX crypto key", shareText = "Send crypto to my public VaultX key:", trades = "Trades", volume = "Volume", memberSince = "Member since", preferences = "Preferences", priceAlerts = "Price alerts", priceAlertsBody = "Notify me about notable market moves.", confirmations = "Trade confirmations", confirmationsBody = "Keep an extra confirmation before execution.", hideBalances = "Privacy mode", hideBalancesBody = "Hide balances across VaultX.", identity = "Profile identity", 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." },
|
||||
activityTypes = { buy = "Asset purchase", sell = "Asset sale", deposit = "Bank deposit", withdrawal = "Bank withdrawal", transfer_in = "Crypto received", transfer_out = "Crypto sent" },
|
||||
statuses = { completed = "Completed", pending = "Pending review", failed = "Failed", manual_review = "Manual review" },
|
||||
errors = { invalid_profile = "Check your VaultX profile settings.", invalid_handle = "Use 3–20 letters, numbers, dots or underscores.", handle_taken = "That VaultX handle is already taken.", profile_exists = "This character already owns a VaultX profile.", invalid_password = "Password must be 8–72 characters.", password_mismatch = "The passwords do not match.", accept_terms = "Confirm that this is a fictional in-game wallet.", invalid_credentials = "The password is incorrect.", locked = "Too many attempts. Try again later.", not_authenticated = "Sign in to VaultX first.", invalid_amount = "Enter a positive whole amount.", invalid_quantity = "Enter a quantity with up to six decimals.", invalid_wallet_key = "Enter a complete VX receiving key.", recipient_not_found = "No active VaultX account uses this key.", self_transfer = "You cannot send crypto to your own key.", invalid_transfer = "Verify a recipient, asset and valid crypto amount.", recipient_limit_exceeded = "The recipient wallet cannot hold that amount.", insufficient_funds = "Your available balance is too low.", insufficient_liquidity = "The exchange cannot fill that amount right now.", quote_expired = "This quote expired. Request a new one.", quote_unavailable = "The quote is no longer available.", market_unavailable = "This market is temporarily paused.", limit_exceeded = "This request exceeds an exchange limit.", duplicate_request = "This request was already processed.", rate_limited = "Too many requests. Try again shortly.", settlement_pending = "A money transfer is already pending review.", service_unavailable = "VaultX is temporarily unavailable.", request_failed = "The secure exchange request failed.", default = "VaultX could not complete the request." },
|
||||
|
||||
Reference in New Issue
Block a user