diff --git a/android/App.js b/android/App.js
index b414139..670629d 100644
--- a/android/App.js
+++ b/android/App.js
@@ -59,6 +59,7 @@ import {
SafeAreaProvider,
useSafeAreaInsets,
} from "react-native-safe-area-context";
+import { LinearGradient } from "expo-linear-gradient";
// Replaced @gorhom/bottom-sheet with SimpleActionSheet
import SimpleActionSheet from "./components/SimpleActionSheet";
import TokenIcon from "./src/components/TokenIcon";
@@ -2091,19 +2092,19 @@ function AppContent() {
const handleCreateNewWallet = async () => {
setShowAddWalletModal(false);
- // If master seed phrase already exists, create wallet directly without showing seed phrase
+ // Always show seed phrase modal when creating wallet
+ // If master seed phrase already exists, use it; otherwise generate new one
if (masterSeedPhrase) {
- console.log("Master seed phrase exists, creating wallet directly");
- await handleConfirmCreateWallet();
- return;
+ console.log("Master seed phrase exists, showing it for backup");
+ setNewMnemonic(masterSeedPhrase);
+ } else {
+ // First wallet - generate master seed phrase and show it for backup
+ const newMasterSeed = bip39.generateMnemonic();
+ setMasterSeedPhrase(newMasterSeed);
+ await saveMasterSeedPhrase(newMasterSeed);
+ setNewMnemonic(newMasterSeed);
+ console.log("Generated and saved new master seed phrase");
}
-
- // First wallet - generate master seed phrase and show it for backup
- const newMasterSeed = bip39.generateMnemonic();
- setMasterSeedPhrase(newMasterSeed);
- await saveMasterSeedPhrase(newMasterSeed);
- setNewMnemonic(newMasterSeed);
- console.log("Generated and saved new master seed phrase");
setShowCreateWalletModal(true);
};
@@ -3430,6 +3431,66 @@ function AppContent() {
);
}
+ // Show empty state when no wallets exist (but not when modals are open)
+ if (authState === "unlocked" && wallets.length === 0 && !showCreateWalletModal && !showAddWalletModal && !showImportWalletModal) {
+ return (
+
+
+
+ {/* Dark blue to transparent gradient overlay */}
+
+
+ {/* Logo with blur background */}
+
+
+
+
+
+ {/* Title */}
+ X1 Wallet
+
+ {/* Buttons */}
+
+ {
+ triggerHaptic();
+ await handleCreateNewWallet();
+ }}
+ >
+ Create Wallet
+
+
+ {
+ triggerHaptic();
+ setShowAddWalletModal(true);
+ }}
+ >
+ Import Wallet
+
+
+
+
+ );
+ }
+
return (
<>
@@ -7815,6 +7876,93 @@ const styles = StyleSheet.create({
color: "#4A90E2",
fontWeight: "600",
},
+ // Empty State Styles
+ emptyStateContainer: {
+ flex: 1,
+ position: "relative",
+ },
+ emptyStateBackground: {
+ position: "absolute",
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ width: "100%",
+ height: "100%",
+ },
+ emptyStateGradientOverlay: {
+ position: "absolute",
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ width: "100%",
+ height: "100%",
+ },
+ emptyStateContent: {
+ flex: 1,
+ justifyContent: "center",
+ alignItems: "center",
+ paddingHorizontal: 40,
+ },
+ emptyStateLogoContainer: {
+ position: "relative",
+ alignItems: "center",
+ justifyContent: "center",
+ marginBottom: 24,
+ },
+ emptyStateLogoBlur: {
+ position: "absolute",
+ width: 120,
+ height: 120,
+ zIndex: 0,
+ },
+ emptyStateLogo: {
+ width: 120,
+ height: 120,
+ zIndex: 1,
+ },
+ emptyStateTitle: {
+ fontSize: 32,
+ fontWeight: "bold",
+ color: "#FFFFFF",
+ marginBottom: 60,
+ textAlign: "center",
+ },
+ emptyStateButtons: {
+ width: "100%",
+ },
+ emptyStateCreateButton: {
+ backgroundColor: "#4A90E2",
+ borderRadius: 12,
+ paddingVertical: 16,
+ paddingHorizontal: 24,
+ alignItems: "center",
+ justifyContent: "center",
+ width: "100%",
+ marginBottom: 16,
+ },
+ emptyStateCreateButtonText: {
+ fontSize: 16,
+ fontWeight: "600",
+ color: "#FFFFFF",
+ },
+ emptyStateImportButton: {
+ backgroundColor: "#1a1a1a",
+ borderRadius: 12,
+ paddingVertical: 16,
+ paddingHorizontal: 24,
+ alignItems: "center",
+ justifyContent: "center",
+ width: "100%",
+ borderWidth: 1,
+ borderColor: "#333333",
+ },
+ emptyStateImportButtonText: {
+ fontSize: 16,
+ fontWeight: "600",
+ color: "#FFFFFF",
+ },
// QR Scanner styles
scanIcon: {
width: 24,
@@ -7832,9 +7980,6 @@ const styles = StyleSheet.create({
qrOverlayTop: {
flex: 1,
backgroundColor: "rgba(0, 0, 0, 0.6)",
- justifyContent: "center",
- alignItems: "center",
- paddingHorizontal: 40,
},
qrOverlayMiddle: {
flexDirection: "row",
diff --git a/android/assets/bg.png b/android/assets/bg.png
new file mode 100644
index 0000000..df8463a
Binary files /dev/null and b/android/assets/bg.png differ
diff --git a/android/assets/x1-logo-with-blur.png b/android/assets/x1-logo-with-blur.png
new file mode 100644
index 0000000..daf54c3
Binary files /dev/null and b/android/assets/x1-logo-with-blur.png differ
diff --git a/android/assets/x1-wallet.png b/android/assets/x1-wallet.png
new file mode 100644
index 0000000..eefceb1
Binary files /dev/null and b/android/assets/x1-wallet.png differ
diff --git a/android/clean_build.sh b/android/clean_build.sh
new file mode 100755
index 0000000..49a3bce
--- /dev/null
+++ b/android/clean_build.sh
@@ -0,0 +1,2 @@
+!#bin/sh
+cd /Users/wei/projects/backpack-android-2/android/android && rm -rf app/.cxx app/build .gradle build && find . -name ".cxx" -type d -exec rm -rf {} + 2>/dev/null || true
diff --git a/android/package-lock.json b/android/package-lock.json
index fa51fc1..a5066e2 100644
--- a/android/package-lock.json
+++ b/android/package-lock.json
@@ -29,6 +29,7 @@
"expo": "~54.0.23",
"expo-camera": "^17.0.9",
"expo-clipboard": "~8.0.7",
+ "expo-linear-gradient": "^15.0.7",
"expo-local-authentication": "^17.0.7",
"expo-secure-store": "~15.0.7",
"expo-status-bar": "~3.0.8",
@@ -76,7 +77,6 @@
"resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.14.0.tgz",
"integrity": "sha512-0YQKKRIxiMlIou+SekQqdCo0ZTHxOcES+K8vKB53cIDpwABNR0P0yRzPgsbgcj3zRJniD93S/ontsnZsCLZrxQ==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@graphql-typed-document-node/core": "^3.1.1",
"@wry/caches": "^1.0.0",
@@ -142,7 +142,6 @@
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5",
@@ -3224,7 +3223,6 @@
"integrity": "sha512-gXlD85hqDDC0C1e2on0pTJoV6pOL3ZJBRDbAzAqSa8Q6Y76tHWqsNTTBuipTjzr/9yFxqPp96p/i/P6lMMsvLg==",
"hasInstallScript": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"canvaskit-wasm": "0.40.0",
"react-reconciler": "0.31.0"
@@ -3482,7 +3480,6 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.17.tgz",
"integrity": "sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA==",
"license": "MIT",
- "peer": true,
"dependencies": {
"csstype": "^3.0.2"
}
@@ -4318,7 +4315,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.8.19",
"caniuse-lite": "^1.0.30001751",
@@ -5432,7 +5428,6 @@
"resolved": "https://registry.npmjs.org/expo/-/expo-54.0.23.tgz",
"integrity": "sha512-b4uQoiRwQ6nwqsT2709RS15CWYNGF3eJtyr1KyLw9WuMAK7u4jjofkhRiO0+3o1C2NbV+WooyYTOZGubQQMBaQ==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/runtime": "^7.20.0",
"@expo/cli": "54.0.16",
@@ -5511,6 +5506,17 @@
"react-native": "*"
}
},
+ "node_modules/expo-linear-gradient": {
+ "version": "15.0.7",
+ "resolved": "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-15.0.7.tgz",
+ "integrity": "sha512-yF+y+9Shpr/OQFfy/wglB/0bykFMbwHBTuMRa5Of/r2P1wbkcacx8rg0JsUWkXH/rn2i2iWdubyqlxSJa3ggZA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "expo": "*",
+ "react": "*",
+ "react-native": "*"
+ }
+ },
"node_modules/expo-local-authentication": {
"version": "17.0.7",
"resolved": "https://registry.npmjs.org/expo-local-authentication/-/expo-local-authentication-17.0.7.tgz",
@@ -5931,7 +5937,6 @@
"resolved": "https://registry.npmjs.org/expo-font/-/expo-font-14.0.9.tgz",
"integrity": "sha512-xCoQbR/36qqB6tew/LQ6GWICpaBmHLhg/Loix5Rku/0ZtNaXMJv08M9o1AcrdiGTn/Xf/BnLu6DgS45cWQEHZg==",
"license": "MIT",
- "peer": true,
"dependencies": {
"fontfaceobserver": "^2.1.0"
},
@@ -6417,7 +6422,6 @@
"resolved": "https://registry.npmjs.org/graphql/-/graphql-16.12.0.tgz",
"integrity": "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==",
"license": "MIT",
- "peer": true,
"engines": {
"node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
}
@@ -9436,7 +9440,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -9474,7 +9477,6 @@
"resolved": "https://registry.npmjs.org/react-native/-/react-native-0.81.5.tgz",
"integrity": "sha512-1w+/oSjEXZjMqsIvmkCRsOc8UBYv163bTWKTI8+1mxztvQPhCRYGTvZ/PL1w16xXHneIj/SLGfxWg2GWN2uexw==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@jest/create-cache-key-function": "^29.7.0",
"@react-native/assets-registry": "0.81.5",
@@ -9556,7 +9558,6 @@
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.28.0.tgz",
"integrity": "sha512-0msfJ1vRxXKVgTgvL+1ZOoYw3/0z1R+Ked0+udoJhyplC2jbVKIJ8Z1bzWdpQRCV3QcQ87Op0zJVE5DhKK2A0A==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@egjs/hammerjs": "^2.0.17",
"hoist-non-react-statics": "^3.3.0",
@@ -9669,7 +9670,6 @@
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-4.1.3.tgz",
"integrity": "sha512-GP8wsi1u3nqvC1fMab/m8gfFwFyldawElCcUSBJQgfrXeLmsPPUOpDw44lbLeCpcwUuLa05WTVePdTEwCLTUZg==",
"license": "MIT",
- "peer": true,
"dependencies": {
"react-native-is-edge-to-edge": "^1.2.1",
"semver": "7.7.2"
@@ -9698,7 +9698,6 @@
"resolved": "git+ssh://git@github.com/AppAndFlow/react-native-safe-area-context.git",
"integrity": "sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg==",
"license": "MIT",
- "peer": true,
"peerDependencies": {
"react": "*",
"react-native": "*"
@@ -9723,7 +9722,6 @@
"resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.12.1.tgz",
"integrity": "sha512-vCuZJDf8a5aNC2dlMovEv4Z0jjEUET53lm/iILFnFewa15b4atjVxU6Wirm6O9y6dEsdjDZVD7Q3QM4T1wlI8g==",
"license": "MIT",
- "peer": true,
"dependencies": {
"css-select": "^5.1.0",
"css-tree": "^1.1.3",
@@ -9749,7 +9747,6 @@
"resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.16.0.tgz",
"integrity": "sha512-Nh13xKZWW35C0dbOskD7OX01nQQavOzHbCw9XoZmar4eXCo7AvrYJ0jlUfRVVIJzqINxHlpECYLdmAdFsl9xDA==",
"license": "MIT",
- "peer": true,
"dependencies": {
"escape-string-regexp": "^4.0.0",
"invariant": "2.2.4"
@@ -9764,7 +9761,6 @@
"resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.5.1.tgz",
"integrity": "sha512-lJG6Uk9YuojjEX/tQrCbcbmpdLCSFxDK1rJlkDhgqkVi1KZzG7cdcBFQRqyNOOzR9Y0CXNuldmtWTGOyM0k0+w==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/plugin-transform-arrow-functions": "^7.0.0-0",
"@babel/plugin-transform-class-properties": "^7.0.0-0",
@@ -9895,7 +9891,6 @@
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
"integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==",
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -11220,7 +11215,6 @@
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"license": "Apache-2.0",
- "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -11669,7 +11663,6 @@
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=8.3.0"
},
@@ -11857,7 +11850,6 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT",
- "peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
diff --git a/android/package.json b/android/package.json
index 63d8296..368e4bc 100644
--- a/android/package.json
+++ b/android/package.json
@@ -30,6 +30,7 @@
"expo": "~54.0.23",
"expo-camera": "^17.0.9",
"expo-clipboard": "~8.0.7",
+ "expo-linear-gradient": "^15.0.7",
"expo-local-authentication": "^17.0.7",
"expo-secure-store": "~15.0.7",
"expo-status-bar": "~3.0.8",
diff --git a/android/src/auth/PinDots.js b/android/src/auth/PinDots.js
index a615b65..c4458dd 100644
--- a/android/src/auth/PinDots.js
+++ b/android/src/auth/PinDots.js
@@ -18,16 +18,16 @@ const styles = StyleSheet.create({
container: {
flexDirection: "row",
justifyContent: "center",
- gap: 15,
- marginVertical: 30,
+ marginVertical: 16,
},
dot: {
- width: 16,
- height: 16,
- borderRadius: 8,
+ width: 14,
+ height: 14,
+ borderRadius: 7,
borderWidth: 2,
- borderColor: "rgba(255, 255, 255, 0.3)",
+ borderColor: "#FFFFFF",
backgroundColor: "transparent",
+ marginHorizontal: 8,
},
dotFilled: {
backgroundColor: "#FFFFFF",
diff --git a/android/src/auth/PinPad.js b/android/src/auth/PinPad.js
index 8b802be..ab62e08 100644
--- a/android/src/auth/PinPad.js
+++ b/android/src/auth/PinPad.js
@@ -2,62 +2,84 @@ import React from "react";
import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
export const PinPad = ({ onNumberPress, onBackspace }) => {
- const numbers = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "", "0", "⌫"];
+ const renderButton = (num, index) => {
+ if (num === "") {
+ return ;
+ }
- return (
-
- {numbers.map((num, index) => {
- if (num === "") {
- return ;
- }
+ if (num === "←") {
+ return (
+
+ {num}
+
+ );
+ }
- if (num === "⌫") {
- return (
-
- {num}
-
- );
- }
+ return (
+ onNumberPress(num)}
+ activeOpacity={0.7}
+ >
+ {num}
+
+ );
+ };
- return (
- onNumberPress(num)}
- activeOpacity={0.7}
- >
- {num}
-
- );
- })}
+ return (
+
+ {/* Row 1: 1, 2, 3 */}
+
+ {["1", "2", "3"].map((num, index) => renderButton(num, index))}
+
+ {/* Row 2: 4, 5, 6 */}
+
+ {["4", "5", "6"].map((num, index) => renderButton(num, index + 3))}
+
+ {/* Row 3: 7, 8, 9 */}
+
+ {["7", "8", "9"].map((num, index) => renderButton(num, index + 6))}
+
+ {/* Row 4: empty, 0, backspace */}
+
+ {renderButton("", 9)}
+ {renderButton("0", 10)}
+ {renderButton("←", 11)}
+
);
};
const styles = StyleSheet.create({
container: {
+ width: "100%",
+ maxWidth: 300,
+ alignItems: "center",
+ paddingHorizontal: 20,
+ },
+ row: {
flexDirection: "row",
- flexWrap: "wrap",
- width: 300,
justifyContent: "center",
+ marginBottom: 12,
},
button: {
- width: 90,
- height: 70,
+ width: 80,
+ height: 60,
justifyContent: "center",
alignItems: "center",
- margin: 5,
- borderRadius: 45,
- backgroundColor: "rgba(255, 255, 255, 0.1)",
+ marginHorizontal: 8,
+ borderRadius: 12,
+ backgroundColor: "rgba(26, 26, 26, 0.8)",
},
buttonText: {
color: "#FFFFFF",
- fontSize: 28,
- fontWeight: "300",
+ fontSize: 24,
+ fontWeight: "400",
},
});
diff --git a/android/src/auth/PinSetup.js b/android/src/auth/PinSetup.js
index 749b6ee..394ddf3 100644
--- a/android/src/auth/PinSetup.js
+++ b/android/src/auth/PinSetup.js
@@ -1,11 +1,14 @@
import React, { useState } from "react";
-import { View, Text, StyleSheet, Alert } from "react-native";
+import { View, Text, StyleSheet, Alert, Image } from "react-native";
+import { LinearGradient } from "expo-linear-gradient";
+import { useSafeAreaInsets } from "react-native-safe-area-context";
import { PinPad } from "./PinPad";
import { PinDots } from "./PinDots";
import { AuthManager } from "./AuthManager";
import { Toast } from "./Toast";
export const PinSetup = ({ password, onComplete }) => {
+ const insets = useSafeAreaInsets();
const [pin, setPin] = useState("");
const [confirmPin, setConfirmPin] = useState("");
const [step, setStep] = useState("enter"); // 'enter' or 'confirm'
@@ -83,23 +86,54 @@ export const PinSetup = ({ password, onComplete }) => {
return (
-
-
- {step === "enter" ? "Create PIN" : "Confirm PIN"}
-
-
- {step === "enter"
- ? "Enter a 6-digit PIN to secure your wallet"
- : "Re-enter your PIN to confirm"}
-
-
-
-
+ {/* Dark blue to transparent gradient overlay */}
+
+
+ {/* Logo with blur background */}
+
+
+
+
+
+ {/* Header */}
+
+
+ {step === "enter" ? "Create PIN" : "Confirm PIN"}
+
+
+ {step === "enter"
+ ? "Enter a 6-digit PIN to secure your wallet"
+ : "Re-enter your PIN to confirm"}
+
+
+
+ {/* PIN Dots */}
+
-
+ {/* Keypad */}
+
+
+
+
{
const styles = StyleSheet.create({
container: {
flex: 1,
- backgroundColor: "#111827",
+ position: "relative",
+ },
+ backgroundImage: {
+ position: "absolute",
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ width: "100%",
+ height: "100%",
+ },
+ gradientOverlay: {
+ position: "absolute",
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ width: "100%",
+ height: "100%",
+ },
+ content: {
+ flex: 1,
justifyContent: "center",
alignItems: "center",
- padding: 20,
+ paddingHorizontal: 20,
},
- header: {
+ logoContainer: {
+ position: "relative",
alignItems: "center",
+ justifyContent: "center",
marginBottom: 20,
},
+ logoBlur: {
+ position: "absolute",
+ width: 100,
+ height: 100,
+ zIndex: 0,
+ },
+ logo: {
+ width: 100,
+ height: 100,
+ zIndex: 1,
+ },
+ header: {
+ alignItems: "center",
+ marginBottom: 16,
+ },
title: {
- fontSize: 28,
+ fontSize: 32,
fontWeight: "bold",
color: "#FFFFFF",
- marginBottom: 10,
+ marginBottom: 8,
},
subtitle: {
fontSize: 14,
@@ -135,4 +207,9 @@ const styles = StyleSheet.create({
textAlign: "center",
paddingHorizontal: 40,
},
+ keypadContainer: {
+ marginTop: 20,
+ width: "100%",
+ alignItems: "center",
+ },
});
diff --git a/android/src/auth/PinUnlock.js b/android/src/auth/PinUnlock.js
index fdc798b..88d93ed 100644
--- a/android/src/auth/PinUnlock.js
+++ b/android/src/auth/PinUnlock.js
@@ -7,12 +7,15 @@ import {
Alert,
Image,
} from "react-native";
+import { LinearGradient } from "expo-linear-gradient";
+import { useSafeAreaInsets } from "react-native-safe-area-context";
import { PinPad } from "./PinPad";
import { PinDots } from "./PinDots";
import { AuthManager, PinLockoutError } from "./AuthManager";
import { Toast } from "./Toast";
export const PinUnlock = ({ onUnlock }) => {
+ const insets = useSafeAreaInsets();
const [pin, setPin] = useState("");
const [error, setError] = useState("");
const [lockoutMs, setLockoutMs] = useState(0);
@@ -161,31 +164,63 @@ export const PinUnlock = ({ onUnlock }) => {
return (
-
- Enter PIN
-
- {lockoutMs > 0
- ? `Locked for ${formatLockoutTime(lockoutMs)}`
- : "Enter your 6-digit PIN to unlock"}
-
-
-
-
-
- {biometricAvailable && lockoutMs === 0 && (
-
+
+ {/* Dark blue to transparent gradient overlay */}
+
+
+ {/* Logo with blur background */}
+
-
- )}
+
+
+
+ {/* Header */}
+
+ Enter PIN
+
+ {lockoutMs > 0
+ ? `Locked for ${formatLockoutTime(lockoutMs)}`
+ : "Enter your 6-digit PIN to unlock"}
+
+
+
+ {/* PIN Dots */}
+
-
+ {/* Biometric button (if available) */}
+ {biometricAvailable && lockoutMs === 0 && (
+
+
+
+ )}
+
+ {/* Keypad */}
+
+
+
+
{
const styles = StyleSheet.create({
container: {
flex: 1,
- backgroundColor: "#111827",
+ position: "relative",
+ },
+ backgroundImage: {
+ position: "absolute",
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ width: "100%",
+ height: "100%",
+ },
+ gradientOverlay: {
+ position: "absolute",
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ width: "100%",
+ height: "100%",
+ },
+ content: {
+ flex: 1,
justifyContent: "center",
alignItems: "center",
- padding: 20,
+ paddingHorizontal: 20,
+ },
+ logoContainer: {
+ position: "relative",
+ alignItems: "center",
+ justifyContent: "center",
+ marginBottom: 20,
+ },
+ logoBlur: {
+ position: "absolute",
+ width: 100,
+ height: 100,
+ zIndex: 0,
+ },
+ logo: {
+ width: 100,
+ height: 100,
+ zIndex: 1,
},
header: {
alignItems: "center",
- marginBottom: 10,
+ marginBottom: 16,
},
title: {
- fontSize: 28,
+ fontSize: 32,
fontWeight: "bold",
color: "#FFFFFF",
- marginBottom: 10,
+ marginBottom: 8,
},
subtitle: {
fontSize: 14,
@@ -229,8 +302,8 @@ const styles = StyleSheet.create({
},
biometricButton: {
alignSelf: "center",
- marginTop: -10,
- marginBottom: 15,
+ marginTop: 8,
+ marginBottom: 16,
padding: 10,
},
fingerprintImage: {
@@ -238,4 +311,9 @@ const styles = StyleSheet.create({
height: 95,
opacity: 0.5,
},
+ keypadContainer: {
+ marginTop: 20,
+ width: "100%",
+ alignItems: "center",
+ },
});
diff --git a/android/yarn.lock b/android/yarn.lock
index 0a4e2e2..2208af5 100644
--- a/android/yarn.lock
+++ b/android/yarn.lock
@@ -3123,6 +3123,11 @@ expo-keep-awake@~15.0.7:
resolved "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-15.0.7.tgz"
integrity sha512-CgBNcWVPnrIVII5G54QDqoE125l+zmqR4HR8q+MQaCfHet+dYpS5vX5zii/RMayzGN4jPgA4XYIQ28ePKFjHoA==
+expo-linear-gradient@^15.0.7:
+ version "15.0.7"
+ resolved "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-15.0.7.tgz"
+ integrity sha512-yF+y+9Shpr/OQFfy/wglB/0bykFMbwHBTuMRa5Of/r2P1wbkcacx8rg0JsUWkXH/rn2i2iWdubyqlxSJa3ggZA==
+
expo-local-authentication@^17.0.7:
version "17.0.7"
resolved "https://registry.npmjs.org/expo-local-authentication/-/expo-local-authentication-17.0.7.tgz"