Steven Frontend

This commit is contained in:
2026-01-26 00:34:58 -04:00
parent 7944b9f7ed
commit b8c241c1a0
6 changed files with 1977 additions and 108 deletions

42
package-lock.json generated
View File

@@ -76,6 +76,7 @@
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz",
"integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.28.6",
"@babel/generator": "^7.28.6",
@@ -479,7 +480,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz",
"integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/traverse": "^7.28.5"
@@ -496,7 +496,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz",
"integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -512,7 +511,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz",
"integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -528,7 +526,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz",
"integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
@@ -546,7 +543,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz",
"integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.28.6",
"@babel/traverse": "^7.28.6"
@@ -647,7 +643,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
"integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=6.9.0"
},
@@ -768,7 +763,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz",
"integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.28.6"
},
@@ -955,7 +949,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
"integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.18.6",
"@babel/helper-plugin-utils": "^7.18.6"
@@ -1021,7 +1014,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz",
"integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1068,7 +1060,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz",
"integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-create-class-features-plugin": "^7.28.6",
"@babel/helper-plugin-utils": "^7.28.6"
@@ -1137,7 +1128,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz",
"integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.28.5",
"@babel/helper-plugin-utils": "^7.28.6"
@@ -1154,7 +1144,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz",
"integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1170,7 +1159,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.28.6.tgz",
"integrity": "sha512-5suVoXjC14lUN6ZL9OLKIHCNVWCrqGqlmEp/ixdXjvgnEl/kauLvvMO/Xw9NyMc95Joj1AeLVPVMvibBgSoFlA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.28.5",
"@babel/helper-plugin-utils": "^7.28.6"
@@ -1187,7 +1175,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz",
"integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1203,7 +1190,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz",
"integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.28.6",
"@babel/plugin-transform-destructuring": "^7.28.5"
@@ -1220,7 +1206,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz",
"integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.28.6"
},
@@ -1300,7 +1285,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz",
"integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.28.6"
},
@@ -1346,7 +1330,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz",
"integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1362,7 +1345,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz",
"integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-module-transforms": "^7.27.1",
"@babel/helper-plugin-utils": "^7.27.1"
@@ -1395,7 +1377,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.28.5.tgz",
"integrity": "sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-module-transforms": "^7.28.3",
"@babel/helper-plugin-utils": "^7.27.1",
@@ -1414,7 +1395,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz",
"integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-module-transforms": "^7.27.1",
"@babel/helper-plugin-utils": "^7.27.1"
@@ -1447,7 +1427,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz",
"integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1512,7 +1491,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz",
"integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/helper-replace-supers": "^7.27.1"
@@ -1608,7 +1586,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz",
"integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1734,7 +1711,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz",
"integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.28.5",
"@babel/helper-plugin-utils": "^7.28.6"
@@ -1751,7 +1727,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz",
"integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1848,7 +1823,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz",
"integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1883,7 +1857,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz",
"integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1899,7 +1872,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz",
"integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.28.5",
"@babel/helper-plugin-utils": "^7.28.6"
@@ -1932,7 +1904,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz",
"integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.28.5",
"@babel/helper-plugin-utils": "^7.28.6"
@@ -2051,7 +2022,6 @@
"resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
"integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/types": "^7.4.4",
@@ -2669,6 +2639,7 @@
"resolved": "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-4.0.1.tgz",
"integrity": "sha512-CRpbLvdJ1T42S+lrYa1iZp1KfDeBp4oeZOK3hdpiS5n0vR0nhD6sC1gGF0sTboCTp64tLteikz5Y3j53dvgOIw==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"react-native": "*"
}
@@ -3701,6 +3672,7 @@
"resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-6.1.18.tgz",
"integrity": "sha512-mIT9MiL/vMm4eirLcmw2h6h/Nm5FICtnYSdohq4vTLA2FF/6PNhByM7s8ffqoVfE5L0uAa6Xda1B7oddolUiGg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@react-navigation/core": "^6.4.17",
"escape-string-regexp": "^4.0.0",
@@ -3865,6 +3837,7 @@
"integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.2.2"
@@ -4467,6 +4440,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -5482,7 +5456,6 @@
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"license": "BSD-2-Clause",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -5607,6 +5580,7 @@
"resolved": "https://registry.npmjs.org/expo/-/expo-52.0.48.tgz",
"integrity": "sha512-/HR/vuo57KGEWlvF3GWaquwEAjXuA5hrOCsaLcZ3pMSA8mQ27qKd1jva4GWzpxXYedlzs/7LLP1XpZo6hXTsog==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.20.0",
"@expo/cli": "0.22.27",
@@ -9079,6 +9053,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0"
},
@@ -9162,6 +9137,7 @@
"resolved": "https://registry.npmjs.org/react-native/-/react-native-0.76.9.tgz",
"integrity": "sha512-+LRwecWmTDco7OweGsrECIqJu0iyrREd6CTCgC/uLLYipiHvk+MH9nd6drFtCw/6Blz6eoKTcH9YTTJusNtrWg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jest/create-cache-key-function": "^29.6.3",
"@react-native/assets-registry": "0.76.9",
@@ -9263,6 +9239,7 @@
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.12.0.tgz",
"integrity": "sha512-ukk5PxcF4p3yu6qMZcmeiZgowhb5AsKRnil54YFUUAXVIS7PJcMHGGC+q44fCiBg44/1AJk5njGMez1m9H0BVQ==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"react": "*",
"react-native": "*"
@@ -9273,6 +9250,7 @@
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.4.0.tgz",
"integrity": "sha512-c7zc7Zwjty6/pGyuuvh9gK3YBYqHPOxrhXfG1lF4gHlojQSmIx2piNbNaV+Uykj+RDTmFXK0e/hA+fucw/Qozg==",
"license": "MIT",
"peer": true,
"dependencies": {
"react-freeze": "^1.0.0",
"warn-once": "^0.1.0"

View File

@@ -6,7 +6,6 @@ import { colors, borderRadius, typography } from '../theme/colors';
// Screens
import FlowScreen from '../screens/FlowScreen';
import VaultScreen from '../screens/VaultScreen';
import SentinelScreen from '../screens/SentinelScreen';
import HeritageScreen from '../screens/HeritageScreen';
import MeScreen from '../screens/MeScreen';
@@ -89,22 +88,6 @@ export default function TabNavigator() {
),
}}
/>
<Tab.Screen
name="Vault"
component={VaultScreen}
options={{
tabBarIcon: ({ focused }) => (
<TabBarItem
name="Vault"
label="Vault"
focused={focused}
color={focused ? colors.vault.primary : colors.vault.textSecondary}
isDark
/>
),
tabBarStyle: styles.tabBarDark,
}}
/>
<Tab.Screen
name="Sentinel"
component={SentinelScreen}

View File

@@ -13,6 +13,7 @@ import { LinearGradient } from 'expo-linear-gradient';
import { Ionicons, Feather, MaterialCommunityIcons, FontAwesome5 } from '@expo/vector-icons';
import { colors, typography, spacing, borderRadius, shadows } from '../theme/colors';
import { Heir, HeirStatus, PaymentStrategy } from '../types';
import VaultScreen from './VaultScreen';
// Mock heirs data
const initialHeirs: Heir[] = [
@@ -20,6 +21,7 @@ const initialHeirs: Heir[] = [
id: '1',
name: 'John Smith',
email: 'john.smith@email.com',
phone: '+1 415 555 0132',
status: 'confirmed',
releaseLevel: 3,
releaseOrder: 1,
@@ -29,6 +31,7 @@ const initialHeirs: Heir[] = [
id: '2',
name: 'Jane Doe',
email: 'jane.doe@email.com',
phone: '+1 212 555 0184',
status: 'confirmed',
releaseLevel: 2,
releaseOrder: 2,
@@ -38,6 +41,7 @@ const initialHeirs: Heir[] = [
id: '3',
name: 'Alice Johnson',
email: 'alice.j@email.com',
phone: '+1 646 555 0149',
status: 'invited',
releaseLevel: 1,
releaseOrder: 3,
@@ -69,6 +73,7 @@ export default function HeritageScreen() {
const [showAddModal, setShowAddModal] = useState(false);
const [showDetailModal, setShowDetailModal] = useState(false);
const [selectedHeir, setSelectedHeir] = useState<Heir | null>(null);
const [showVault, setShowVault] = useState(false);
const [newHeirName, setNewHeirName] = useState('');
const [newHeirEmail, setNewHeirEmail] = useState('');
const [newHeirLevel, setNewHeirLevel] = useState(1);
@@ -147,6 +152,26 @@ export default function HeritageScreen() {
</View>
</View>
{/* Shadow Vault Access */}
<View style={styles.vaultAccessCard}>
<View style={styles.vaultAccessIcon}>
<MaterialCommunityIcons name="treasure-chest" size={22} color={colors.nautical.teal} />
</View>
<View style={styles.vaultAccessContent}>
<Text style={styles.vaultAccessTitle}>Shadow Vault</Text>
<Text style={styles.vaultAccessText}>
Access sealed assets from inside the Heritage layer.
</Text>
</View>
<TouchableOpacity
style={styles.vaultAccessButton}
onPress={() => setShowVault(true)}
activeOpacity={0.8}
>
<Text style={styles.vaultAccessButtonText}>Open</Text>
</TouchableOpacity>
</View>
{/* Release Stages Info */}
<View style={styles.stagesSection}>
<View style={styles.sectionHeader}>
@@ -366,6 +391,24 @@ export default function HeritageScreen() {
</View>
</Modal>
{/* Vault Modal */}
<Modal
visible={showVault}
animationType="slide"
onRequestClose={() => setShowVault(false)}
>
<View style={styles.vaultModalContainer}>
<VaultScreen />
<TouchableOpacity
style={styles.vaultCloseButton}
onPress={() => setShowVault(false)}
activeOpacity={0.85}
>
<Ionicons name="close" size={20} color={colors.nautical.cream} />
</TouchableOpacity>
</View>
</Modal>
{/* Heir Detail Modal */}
<Modal
visible={showDetailModal}
@@ -504,6 +547,61 @@ const styles = StyleSheet.create({
color: colors.heritage.textSecondary,
lineHeight: typography.fontSize.sm * 1.5,
},
contactHeader: {
flexDirection: 'row',
alignItems: 'center',
gap: spacing.sm,
marginBottom: spacing.sm,
},
contactTitle: {
fontSize: typography.fontSize.base,
fontWeight: '600',
color: colors.heritage.text,
},
vaultAccessCard: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.heritage.cardBackground,
borderRadius: borderRadius.xl,
padding: spacing.base,
marginBottom: spacing.lg,
borderWidth: 1,
borderColor: colors.heritage.cardBorder,
...shadows.soft,
},
vaultAccessIcon: {
width: 44,
height: 44,
borderRadius: 22,
backgroundColor: colors.nautical.lightMint,
alignItems: 'center',
justifyContent: 'center',
marginRight: spacing.md,
},
vaultAccessContent: {
flex: 1,
},
vaultAccessTitle: {
fontSize: typography.fontSize.base,
fontWeight: '600',
color: colors.heritage.text,
marginBottom: 2,
},
vaultAccessText: {
fontSize: typography.fontSize.sm,
color: colors.heritage.textSecondary,
},
vaultAccessButton: {
backgroundColor: colors.nautical.teal,
paddingHorizontal: spacing.md,
paddingVertical: spacing.sm,
borderRadius: borderRadius.full,
},
vaultAccessButtonText: {
color: colors.nautical.cream,
fontWeight: '700',
fontSize: typography.fontSize.sm,
},
stagesSection: {
marginBottom: spacing.lg,
},
@@ -825,6 +923,21 @@ const styles = StyleSheet.create({
justifyContent: 'center',
padding: spacing.lg,
},
vaultModalContainer: {
flex: 1,
backgroundColor: colors.vault.background,
},
vaultCloseButton: {
position: 'absolute',
top: spacing.lg,
right: spacing.lg,
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: 'rgba(26, 58, 74, 0.65)',
alignItems: 'center',
justifyContent: 'center',
},
detailModal: {
backgroundColor: colors.heritage.cardBackground,
borderRadius: borderRadius.xxl,

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,8 @@ import {
TextInput,
SafeAreaView,
Animated,
Linking,
Alert,
} from 'react-native';
import { LinearGradient } from 'expo-linear-gradient';
import { Ionicons, Feather, MaterialCommunityIcons, FontAwesome5 } from '@expo/vector-icons';
@@ -18,7 +20,7 @@ import BiometricModal from '../components/common/BiometricModal';
// Asset type configuration with nautical theme
const assetTypeConfig: Record<VaultAssetType, { icon: string; iconType: 'ionicons' | 'feather' | 'material' | 'fontawesome5'; label: string }> = {
game_account: { icon: 'gamepad', iconType: 'fontawesome5', label: 'Digital Account' },
game_account: { icon: 'account-key', iconType: 'material', label: 'Account Login' },
private_key: { icon: 'key', iconType: 'fontawesome5', label: 'Secret Key' },
document: { icon: 'scroll', iconType: 'fontawesome5', label: 'Document' },
photo: { icon: 'image', iconType: 'ionicons', label: 'Sealed Photo' },
@@ -26,6 +28,13 @@ const assetTypeConfig: Record<VaultAssetType, { icon: string; iconType: 'ionicon
custom: { icon: 'gem', iconType: 'fontawesome5', label: 'Treasure' },
};
const accountProviderOptions = [
{ key: 'bank', label: 'Bank', icon: 'bank', iconType: 'material' as const },
{ key: 'steam', label: 'Steam', icon: 'steam', iconType: 'fontawesome5' as const },
{ key: 'facebook', label: 'Facebook', icon: 'facebook-f', iconType: 'fontawesome5' as const },
{ key: 'custom', label: 'Other', icon: 'shield-account', iconType: 'material' as const },
];
// Mock data
const initialAssets: VaultAsset[] = [
{
@@ -85,6 +94,16 @@ export default function VaultScreen() {
const [showUploadSuccess, setShowUploadSuccess] = useState(false);
const [fadeAnim] = useState(new Animated.Value(0));
const [pulseAnim] = useState(new Animated.Value(1));
const [selectedAsset, setSelectedAsset] = useState<VaultAsset | null>(null);
const [showDetail, setShowDetail] = useState(false);
const [showGuardedBiometric, setShowGuardedBiometric] = useState(false);
const [showKeyPreview, setShowKeyPreview] = useState(false);
const [addStep, setAddStep] = useState(1);
const [addMethod, setAddMethod] = useState<'text' | 'file' | 'scan'>('text');
const [addVerified, setAddVerified] = useState(false);
const [rehearsalConfirmed, setRehearsalConfirmed] = useState(false);
const [showAddBiometric, setShowAddBiometric] = useState(false);
const [accountProvider, setAccountProvider] = useState<'bank' | 'steam' | 'facebook' | 'custom'>('bank');
useEffect(() => {
if (!isUnlocked) {
@@ -129,8 +148,20 @@ export default function VaultScreen() {
setIsUnlocked(true);
};
const resetAddFlow = () => {
setAddStep(1);
setAddMethod('text');
setAddVerified(false);
setRehearsalConfirmed(false);
setSelectedType('custom');
setNewLabel('');
setAccountProvider('bank');
};
const handleAddAsset = () => {
if (!newLabel.trim()) return;
if (!addVerified) return;
if (selectedType === 'private_key' && !rehearsalConfirmed) return;
const newAsset: VaultAsset = {
id: Date.now().toString(),
@@ -145,6 +176,8 @@ export default function VaultScreen() {
setNewLabel('');
setSelectedType('custom');
setShowAddModal(false);
setAddVerified(false);
setRehearsalConfirmed(false);
setShowUploadSuccess(true);
setTimeout(() => setShowUploadSuccess(false), 2500);
@@ -158,6 +191,86 @@ export default function VaultScreen() {
});
};
const handleOpenDetail = (asset: VaultAsset) => {
setSelectedAsset(asset);
setShowDetail(true);
setShowKeyPreview(false);
};
const handleCloseDetail = () => {
setShowDetail(false);
setSelectedAsset(null);
setShowKeyPreview(false);
setShowGuardedBiometric(false);
};
const handleGuardedAccess = () => {
setShowGuardedBiometric(true);
};
const handleGuardedSuccess = () => {
setShowGuardedBiometric(false);
setShowKeyPreview(true);
};
const handleAddVerification = () => {
setShowAddBiometric(true);
};
const handleAddVerificationSuccess = () => {
setShowAddBiometric(false);
setAddVerified(true);
};
const openProviderLogin = async () => {
if (accountProvider === 'bank') {
Alert.alert(
'Bank App Needed',
'Provide the bank app deep link scheme to enable one-tap login.'
);
return;
}
const providerLinks = {
steam: {
app: 'steam://openurl/https://store.steampowered.com/login/',
web: 'https://store.steampowered.com/login/',
},
facebook: {
app: 'fb://facewebmodal/f?href=https://www.facebook.com/login',
web: 'https://www.facebook.com/login',
},
custom: {
app: '',
web: '',
},
} as const;
const target = providerLinks[accountProvider];
if (!target?.app) {
return;
}
const canOpen = await Linking.canOpenURL(target.app);
if (canOpen) {
await Linking.openURL(target.app);
} else if (target.web) {
await Linking.openURL(target.web);
}
};
const selectedConfig = selectedAsset ? assetTypeConfig[selectedAsset.type] : null;
const detailHeading = selectedAsset?.type === 'private_key'
? 'Key Material'
: selectedConfig?.label || 'Vault Asset';
const detailMetaLabel = selectedAsset?.type === 'private_key' ? 'KEY MATERIAL' : 'ASSET CLASS';
const detailMetaValue = selectedAsset?.type === 'private_key'
? '12/24 Words'
: selectedConfig?.label || '--';
const canSeal = !!newLabel.trim()
&& addVerified
&& (selectedType !== 'private_key' || rehearsalConfirmed);
// Lock screen
if (!isUnlocked) {
return (
@@ -253,6 +366,7 @@ export default function VaultScreen() {
key={asset.id}
style={styles.assetCard}
activeOpacity={0.7}
onPress={() => handleOpenDetail(asset)}
>
<View style={styles.assetIconContainer}>
{renderAssetTypeIcon(config, 22, colors.vault.primary)}
@@ -277,7 +391,10 @@ export default function VaultScreen() {
{/* Add Button */}
<TouchableOpacity
style={styles.addButton}
onPress={() => setShowAddModal(true)}
onPress={() => {
resetAddFlow();
setShowAddModal(true);
}}
activeOpacity={0.9}
>
<LinearGradient
@@ -316,78 +433,395 @@ export default function VaultScreen() {
<FontAwesome5 name="gem" size={22} color={colors.nautical.teal} />
<Text style={styles.modalTitle}>Add New Treasure</Text>
</View>
<Text style={styles.modalLabel}>TREASURE TYPE</Text>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
style={styles.typeScroll}
contentContainerStyle={styles.typeScrollContent}
>
{(Object.keys(assetTypeConfig) as VaultAssetType[]).map((type) => {
const config = assetTypeConfig[type];
const isSelected = selectedType === type;
<View style={styles.stepRow}>
{['Type', 'Method', 'Verify'].map((label, index) => {
const stepIndex = index + 1;
const isActive = addStep === stepIndex;
const isDone = addStep > stepIndex;
return (
<TouchableOpacity
key={type}
style={[styles.typeButton, isSelected && styles.typeButtonActive]}
onPress={() => setSelectedType(type)}
>
<View style={[styles.typeIconContainer, isSelected && styles.typeIconContainerActive]}>
{renderAssetTypeIcon(config, 22, isSelected ? colors.nautical.teal : colors.nautical.sage)}
<View key={label} style={styles.stepItem}>
<View style={[
styles.stepCircle,
isActive && styles.stepCircleActive,
isDone && styles.stepCircleDone,
]}>
<Text style={[
styles.stepNumber,
isActive && styles.stepNumberActive,
isDone && styles.stepNumberDone,
]}>
{stepIndex}
</Text>
</View>
<Text style={[styles.typeButtonLabel, isSelected && styles.typeButtonLabelActive]}>
{config.label}
<Text style={[styles.stepLabel, (isActive || isDone) && styles.stepLabelActive]}>
{label}
</Text>
</TouchableOpacity>
</View>
);
})}
</ScrollView>
<Text style={styles.modalLabel}>TREASURE NAME</Text>
<TextInput
style={styles.input}
placeholder="e.g., Main wallet mnemonic"
placeholderTextColor={colors.nautical.sage}
value={newLabel}
onChangeText={setNewLabel}
/>
<View style={styles.encryptionNote}>
<MaterialCommunityIcons name="anchor" size={16} color={colors.nautical.teal} />
<Text style={styles.encryptionNoteText}>
Your treasure will be encrypted and sealed. Original data will be securely destroyed.
</Text>
</View>
{addStep === 1 && (
<>
<Text style={styles.modalLabel}>TREASURE TYPE</Text>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
style={styles.typeScroll}
contentContainerStyle={styles.typeScrollContent}
>
{(Object.keys(assetTypeConfig) as VaultAssetType[]).map((type) => {
const config = assetTypeConfig[type];
const isSelected = selectedType === type;
return (
<TouchableOpacity
key={type}
style={[styles.typeButton, isSelected && styles.typeButtonActive]}
onPress={() => setSelectedType(type)}
>
<View style={[styles.typeIconContainer, isSelected && styles.typeIconContainerActive]}>
{renderAssetTypeIcon(config, 22, isSelected ? colors.nautical.teal : colors.nautical.sage)}
</View>
<Text style={[styles.typeButtonLabel, isSelected && styles.typeButtonLabelActive]}>
{config.label}
</Text>
</TouchableOpacity>
);
})}
</ScrollView>
</>
)}
{addStep === 2 && (
<>
{selectedType !== 'game_account' && (
<>
<Text style={styles.modalLabel}>CAPTURE METHOD</Text>
<View style={styles.methodRow}>
{[
{ key: 'text', label: 'Text', icon: 'text' },
{ key: 'file', label: 'File', icon: 'file-tray' },
{ key: 'scan', label: 'Scan', icon: 'camera' },
].map((item) => {
const isActive = addMethod === item.key;
return (
<TouchableOpacity
key={item.key}
style={[styles.methodButton, isActive && styles.methodButtonActive]}
onPress={() => setAddMethod(item.key as typeof addMethod)}
>
<Ionicons
name={item.icon as any}
size={18}
color={isActive ? colors.nautical.teal : colors.nautical.sage}
/>
<Text style={[styles.methodLabel, isActive && styles.methodLabelActive]}>
{item.label}
</Text>
</TouchableOpacity>
);
})}
</View>
</>
)}
{selectedType === 'game_account' && (
<>
<Text style={styles.modalLabel}>ACCOUNT PROVIDER</Text>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
style={styles.typeScroll}
contentContainerStyle={styles.typeScrollContent}
>
{accountProviderOptions.map((option) => {
const isSelected = accountProvider === option.key;
return (
<TouchableOpacity
key={option.key}
style={[styles.typeButton, isSelected && styles.typeButtonActive]}
onPress={() => setAccountProvider(option.key as typeof accountProvider)}
>
<View style={[styles.typeIconContainer, isSelected && styles.typeIconContainerActive]}>
{renderAssetTypeIcon(
{ icon: option.icon, iconType: option.iconType, label: option.label },
22,
isSelected ? colors.nautical.teal : colors.nautical.sage
)}
</View>
<Text style={[styles.typeButtonLabel, isSelected && styles.typeButtonLabelActive]}>
{option.label}
</Text>
</TouchableOpacity>
);
})}
</ScrollView>
<TouchableOpacity
style={styles.loginButton}
onPress={openProviderLogin}
activeOpacity={0.85}
>
<Ionicons name="log-in-outline" size={18} color={colors.nautical.cream} />
<Text style={styles.loginButtonText}>Open App to Login</Text>
</TouchableOpacity>
</>
)}
<Text style={styles.modalLabel}>TREASURE NAME</Text>
<TextInput
style={styles.input}
placeholder="e.g., Main wallet mnemonic"
placeholderTextColor={colors.nautical.sage}
value={newLabel}
onChangeText={setNewLabel}
/>
<View style={styles.encryptionNote}>
<MaterialCommunityIcons name="anchor" size={16} color={colors.nautical.teal} />
<Text style={styles.encryptionNoteText}>
Data is encrypted on-device. Plaintext is shredded after sealing.
</Text>
</View>
</>
)}
{addStep === 3 && (
<>
<Text style={styles.modalLabel}>IDENTITY VERIFICATION</Text>
<View style={styles.verifyCard}>
<View style={styles.verifyRow}>
<Ionicons name="finger-print" size={18} color={colors.nautical.teal} />
<Text style={styles.verifyText}>Biometric required before sealing.</Text>
</View>
<TouchableOpacity
style={[styles.verifyButton, addVerified && styles.verifyButtonDone]}
onPress={handleAddVerification}
activeOpacity={0.85}
>
<Text style={styles.verifyButtonText}>
{addVerified ? 'Verified' : 'Verify Now'}
</Text>
</TouchableOpacity>
</View>
{selectedType === 'private_key' && (
<TouchableOpacity
style={styles.rehearsalRow}
onPress={() => setRehearsalConfirmed(!rehearsalConfirmed)}
activeOpacity={0.8}
>
<Ionicons
name={rehearsalConfirmed ? 'checkbox' : 'square-outline'}
size={20}
color={rehearsalConfirmed ? colors.nautical.teal : colors.nautical.sage}
/>
<Text style={styles.rehearsalText}>
I rehearsed the mnemonic once (required).
</Text>
</TouchableOpacity>
)}
<View style={styles.encryptionNote}>
<MaterialCommunityIcons name="lock" size={16} color={colors.nautical.teal} />
<Text style={styles.encryptionNoteText}>
Only a masked shard can be revealed, and access is time-limited.
</Text>
</View>
</>
)}
<View style={styles.modalButtons}>
<TouchableOpacity
style={styles.cancelButton}
onPress={() => {
setShowAddModal(false);
setNewLabel('');
if (addStep === 1) {
setShowAddModal(false);
} else {
setAddStep(addStep - 1);
}
}}
>
<Text style={styles.cancelButtonText}>Cancel</Text>
<Text style={styles.cancelButtonText}>
{addStep === 1 ? 'Cancel' : 'Back'}
</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.confirmButton}
onPress={handleAddAsset}
>
<LinearGradient
colors={[colors.nautical.teal, colors.nautical.seafoam]}
style={styles.confirmButtonGradient}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
{addStep < 3 ? (
<TouchableOpacity
style={styles.confirmButton}
onPress={() => setAddStep(addStep + 1)}
>
<MaterialCommunityIcons name="lock" size={18} color="#fff" />
<Text style={styles.confirmButtonText}>Seal Treasure</Text>
</LinearGradient>
</TouchableOpacity>
<LinearGradient
colors={[colors.nautical.teal, colors.nautical.seafoam]}
style={styles.confirmButtonGradient}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
>
<Text style={styles.confirmButtonText}>Continue</Text>
</LinearGradient>
</TouchableOpacity>
) : (
<TouchableOpacity
style={styles.confirmButton}
onPress={handleAddAsset}
activeOpacity={canSeal ? 0.9 : 1}
disabled={!canSeal}
>
<LinearGradient
colors={[colors.nautical.teal, colors.nautical.seafoam]}
style={[
styles.confirmButtonGradient,
!canSeal && styles.confirmButtonGradientDisabled,
]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
>
<MaterialCommunityIcons name="lock" size={18} color="#fff" />
<Text style={styles.confirmButtonText}>Seal Treasure</Text>
</LinearGradient>
</TouchableOpacity>
)}
</View>
</View>
</View>
</Modal>
<BiometricModal
visible={showAddBiometric}
onSuccess={handleAddVerificationSuccess}
onCancel={() => setShowAddBiometric(false)}
title="Confirm Identity"
message="Verify before sealing this treasure"
isDark
/>
{/* Detail Modal */}
<Modal
visible={showDetail}
animationType="slide"
onRequestClose={handleCloseDetail}
>
<View style={styles.detailContainer}>
<LinearGradient
colors={[colors.vault.backgroundGradientStart, colors.vault.backgroundGradientEnd]}
style={styles.detailGradient}
>
<SafeAreaView style={styles.detailSafeArea}>
<View style={styles.detailHeader}>
<TouchableOpacity
style={styles.detailBackButton}
onPress={handleCloseDetail}
activeOpacity={0.8}
>
<Ionicons name="chevron-back" size={20} color={colors.vault.text} />
</TouchableOpacity>
<Text style={styles.detailTitle}>{detailHeading}</Text>
{selectedAsset?.type === 'private_key' && (
<View style={styles.riskBadge}>
<Text style={styles.riskBadgeText}>HIGH-RISK</Text>
</View>
)}
</View>
<ScrollView
style={styles.detailScroll}
contentContainerStyle={styles.detailContent}
showsVerticalScrollIndicator={false}
>
<View style={styles.detailHeroCard}>
<View style={styles.detailHeroIcon}>
<MaterialCommunityIcons name="key-variant" size={28} color={colors.vault.primary} />
</View>
<View style={styles.detailHeroInfo}>
<Text style={styles.detailHeroLabel}>SEALED ASSET</Text>
<Text style={styles.detailHeroTitle}>{selectedAsset?.label}</Text>
<Text style={styles.detailHeroSubtitle}>Encrypted at rest · No plaintext stored</Text>
</View>
</View>
<View style={styles.metaGrid}>
<View style={styles.metaCard}>
<Text style={styles.metaLabel}>CREATED</Text>
<Text style={styles.metaValue}>
{selectedAsset ? formatDate(selectedAsset.createdAt) : '--'}
</Text>
</View>
<View style={styles.metaCard}>
<Text style={styles.metaLabel}>LAST VERIFIED</Text>
<Text style={styles.metaValue}>
{selectedAsset ? formatDate(selectedAsset.updatedAt) : '--'}
</Text>
</View>
<View style={styles.metaCard}>
<Text style={styles.metaLabel}>STORAGE</Text>
<Text style={styles.metaValue}>Cipher Pack</Text>
</View>
<View style={styles.metaCard}>
<Text style={styles.metaLabel}>{detailMetaLabel}</Text>
<Text style={styles.metaValue}>{detailMetaValue}</Text>
</View>
</View>
<View style={styles.actionGroup}>
<Text style={styles.sectionTitle}>CONTROLLED ACTIONS</Text>
<TouchableOpacity style={styles.actionRow} activeOpacity={0.8}>
<Ionicons name="finger-print" size={18} color={colors.vault.primary} />
<Text style={styles.actionText}>View Encrypted Digest</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.actionRow} activeOpacity={0.8}>
<MaterialCommunityIcons name="file-lock" size={18} color={colors.vault.primary} />
<Text style={styles.actionText}>Export Cipher Pack</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.actionRow} activeOpacity={0.8}>
<MaterialCommunityIcons name="timer-reset" size={18} color={colors.vault.primary} />
<Text style={styles.actionText}>Reset Sentinel Timer</Text>
</TouchableOpacity>
</View>
<View style={styles.guardCard}>
<View style={styles.guardHeader}>
<MaterialCommunityIcons name="shield-lock" size={18} color={colors.vault.warning} />
<Text style={styles.guardTitle}>Guarded Reveal</Text>
</View>
<Text style={styles.guardText}>
Plaintext access requires biometric verification and a memory rehearsal step.
</Text>
<TouchableOpacity
style={styles.guardButton}
onPress={handleGuardedAccess}
activeOpacity={0.85}
>
<Text style={styles.guardButtonText}>Begin Verification</Text>
</TouchableOpacity>
{showKeyPreview && (
<View style={styles.previewCard}>
<Text style={styles.previewLabel}>MNEMONIC SHARD (MASKED)</Text>
<Text style={styles.previewValue}>ocean-anchored-ember-veil</Text>
</View>
)}
</View>
<View style={styles.warningCard}>
<Ionicons name="warning" size={18} color={colors.vault.warning} />
<Text style={styles.warningText}>
Any reveal attempt is time-limited and logged. Screenshots are blocked by policy.
</Text>
</View>
</ScrollView>
</SafeAreaView>
</LinearGradient>
</View>
<BiometricModal
visible={showGuardedBiometric}
onSuccess={handleGuardedSuccess}
onCancel={() => setShowGuardedBiometric(false)}
title="Verify Access"
message="Confirm to reveal a masked shard of the mnemonic"
isDark
/>
</Modal>
</View>
);
}
@@ -636,6 +1070,53 @@ const styles = StyleSheet.create({
fontWeight: '600',
color: colors.nautical.navy,
},
stepRow: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: spacing.lg,
},
stepItem: {
alignItems: 'center',
flex: 1,
},
stepCircle: {
width: 28,
height: 28,
borderRadius: 14,
borderWidth: 1,
borderColor: colors.nautical.lightMint,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: colors.nautical.paleAqua,
},
stepCircleActive: {
borderColor: colors.nautical.teal,
backgroundColor: colors.nautical.lightMint,
},
stepCircleDone: {
borderColor: colors.nautical.teal,
backgroundColor: colors.nautical.teal,
},
stepNumber: {
fontSize: typography.fontSize.xs,
color: colors.nautical.sage,
fontWeight: '600',
},
stepNumberActive: {
color: colors.nautical.teal,
},
stepNumberDone: {
color: colors.nautical.cream,
},
stepLabel: {
fontSize: typography.fontSize.xs,
color: colors.nautical.sage,
marginTop: spacing.xs,
},
stepLabelActive: {
color: colors.nautical.teal,
fontWeight: '600',
},
modalLabel: {
fontSize: typography.fontSize.xs,
color: colors.nautical.sage,
@@ -643,6 +1124,49 @@ const styles = StyleSheet.create({
letterSpacing: typography.letterSpacing.wider,
fontWeight: '600',
},
methodRow: {
flexDirection: 'row',
gap: spacing.sm,
marginBottom: spacing.lg,
},
methodButton: {
flex: 1,
paddingVertical: spacing.sm,
borderRadius: borderRadius.lg,
alignItems: 'center',
gap: spacing.xs,
backgroundColor: colors.nautical.paleAqua,
borderWidth: 1,
borderColor: colors.nautical.lightMint,
},
methodButtonActive: {
borderColor: colors.nautical.teal,
backgroundColor: colors.nautical.lightMint,
},
methodLabel: {
fontSize: typography.fontSize.sm,
color: colors.nautical.sage,
fontWeight: '600',
},
methodLabelActive: {
color: colors.nautical.teal,
},
loginButton: {
marginTop: spacing.sm,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
gap: spacing.sm,
backgroundColor: colors.nautical.teal,
paddingVertical: spacing.sm,
borderRadius: borderRadius.full,
},
loginButtonText: {
color: colors.nautical.cream,
fontSize: typography.fontSize.sm,
fontWeight: '700',
letterSpacing: 0.6,
},
typeScroll: {
marginBottom: spacing.lg,
},
@@ -740,9 +1264,267 @@ const styles = StyleSheet.create({
paddingVertical: spacing.md,
gap: spacing.sm,
},
confirmButtonGradientDisabled: {
opacity: 0.5,
},
confirmButtonText: {
fontSize: typography.fontSize.base,
color: '#fff',
fontWeight: '600',
},
verifyCard: {
backgroundColor: colors.nautical.paleAqua,
borderRadius: borderRadius.lg,
padding: spacing.base,
marginBottom: spacing.md,
borderWidth: 1,
borderColor: colors.nautical.lightMint,
gap: spacing.sm,
},
verifyRow: {
flexDirection: 'row',
alignItems: 'center',
gap: spacing.sm,
},
verifyText: {
color: colors.nautical.navy,
fontSize: typography.fontSize.sm,
},
verifyButton: {
alignSelf: 'flex-start',
paddingHorizontal: spacing.md,
paddingVertical: spacing.sm,
borderRadius: borderRadius.full,
backgroundColor: colors.nautical.teal,
},
verifyButtonDone: {
backgroundColor: colors.nautical.sage,
},
verifyButtonText: {
color: colors.nautical.cream,
fontWeight: '700',
},
rehearsalRow: {
flexDirection: 'row',
alignItems: 'center',
gap: spacing.sm,
marginBottom: spacing.md,
},
rehearsalText: {
color: colors.nautical.navy,
fontSize: typography.fontSize.sm,
flex: 1,
},
detailContainer: {
flex: 1,
backgroundColor: colors.vault.background,
},
detailGradient: {
flex: 1,
},
detailSafeArea: {
flex: 1,
},
detailHeader: {
paddingHorizontal: spacing.lg,
paddingTop: spacing.lg,
paddingBottom: spacing.md,
flexDirection: 'row',
alignItems: 'center',
gap: spacing.sm,
},
detailBackButton: {
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: 'rgba(255, 255, 255, 0.08)',
alignItems: 'center',
justifyContent: 'center',
},
detailTitle: {
flex: 1,
fontSize: typography.fontSize.lg,
color: colors.vault.text,
fontWeight: '700',
letterSpacing: typography.letterSpacing.wide,
},
riskBadge: {
backgroundColor: `${colors.vault.warning}25`,
borderRadius: borderRadius.full,
paddingHorizontal: spacing.sm,
paddingVertical: 4,
},
riskBadgeText: {
fontSize: typography.fontSize.xs,
color: colors.vault.warning,
fontWeight: '700',
letterSpacing: 1,
},
detailScroll: {
flex: 1,
},
detailContent: {
padding: spacing.lg,
paddingTop: spacing.sm,
gap: spacing.lg,
},
detailHeroCard: {
backgroundColor: colors.vault.cardBackground,
borderRadius: borderRadius.xl,
padding: spacing.lg,
borderWidth: 1,
borderColor: colors.vault.cardBorder,
flexDirection: 'row',
gap: spacing.base,
alignItems: 'center',
},
detailHeroIcon: {
width: 54,
height: 54,
borderRadius: 27,
backgroundColor: 'rgba(184, 224, 229, 0.15)',
alignItems: 'center',
justifyContent: 'center',
},
detailHeroInfo: {
flex: 1,
},
detailHeroLabel: {
fontSize: typography.fontSize.xs,
color: colors.vault.textSecondary,
letterSpacing: 1,
fontWeight: '600',
marginBottom: 4,
},
detailHeroTitle: {
fontSize: typography.fontSize.lg,
color: colors.vault.text,
fontWeight: '700',
marginBottom: 4,
},
detailHeroSubtitle: {
fontSize: typography.fontSize.sm,
color: colors.vault.textSecondary,
},
metaGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: spacing.md,
},
metaCard: {
width: '48%',
backgroundColor: 'rgba(255, 255, 255, 0.05)',
borderRadius: borderRadius.lg,
padding: spacing.base,
borderWidth: 1,
borderColor: 'rgba(255, 255, 255, 0.08)',
},
metaLabel: {
fontSize: typography.fontSize.xs,
color: colors.vault.textSecondary,
letterSpacing: 1,
marginBottom: spacing.xs,
fontWeight: '600',
},
metaValue: {
fontSize: typography.fontSize.sm,
color: colors.vault.text,
fontWeight: '600',
},
actionGroup: {
gap: spacing.sm,
},
sectionTitle: {
fontSize: typography.fontSize.xs,
color: colors.vault.textSecondary,
letterSpacing: 1,
fontWeight: '700',
},
actionRow: {
flexDirection: 'row',
alignItems: 'center',
gap: spacing.sm,
paddingVertical: spacing.sm,
paddingHorizontal: spacing.md,
borderRadius: borderRadius.lg,
backgroundColor: 'rgba(255, 255, 255, 0.06)',
borderWidth: 1,
borderColor: 'rgba(255, 255, 255, 0.08)',
},
actionText: {
color: colors.vault.text,
fontSize: typography.fontSize.base,
fontWeight: '600',
},
guardCard: {
backgroundColor: 'rgba(229, 115, 115, 0.12)',
borderRadius: borderRadius.lg,
padding: spacing.base,
gap: spacing.sm,
borderWidth: 1,
borderColor: 'rgba(229, 115, 115, 0.3)',
},
guardHeader: {
flexDirection: 'row',
alignItems: 'center',
gap: spacing.sm,
},
guardTitle: {
fontSize: typography.fontSize.base,
color: colors.vault.warning,
fontWeight: '700',
},
guardText: {
fontSize: typography.fontSize.sm,
color: colors.vault.textSecondary,
lineHeight: typography.fontSize.sm * 1.5,
},
guardButton: {
alignSelf: 'flex-start',
backgroundColor: colors.vault.warning,
paddingHorizontal: spacing.md,
paddingVertical: spacing.sm,
borderRadius: borderRadius.full,
},
guardButtonText: {
color: colors.vault.text,
fontWeight: '700',
letterSpacing: 0.6,
},
previewCard: {
backgroundColor: colors.vault.cardBackground,
borderRadius: borderRadius.lg,
padding: spacing.base,
borderWidth: 1,
borderColor: colors.vault.cardBorder,
},
previewLabel: {
fontSize: typography.fontSize.xs,
color: colors.vault.textSecondary,
letterSpacing: 1,
marginBottom: spacing.xs,
fontWeight: '600',
},
previewValue: {
fontFamily: typography.fontFamily.mono,
color: colors.vault.text,
fontSize: typography.fontSize.base,
letterSpacing: 0.6,
},
warningCard: {
flexDirection: 'row',
alignItems: 'flex-start',
gap: spacing.sm,
backgroundColor: 'rgba(26, 58, 74, 0.6)',
borderRadius: borderRadius.lg,
padding: spacing.base,
borderWidth: 1,
borderColor: 'rgba(229, 115, 115, 0.35)',
},
warningText: {
flex: 1,
color: colors.vault.textSecondary,
fontSize: typography.fontSize.sm,
lineHeight: typography.fontSize.sm * 1.5,
},
});

View File

@@ -54,6 +54,7 @@ export interface Heir {
id: string;
name: string;
email: string;
phone?: string;
status: HeirStatus;
releaseLevel: number; // 1-3
releaseOrder: number;