diff --git a/metro.config.js b/metro.config.js
index 40eff0a..f28aedf 100644
--- a/metro.config.js
+++ b/metro.config.js
@@ -1,5 +1,26 @@
const {getDefaultConfig} = require('expo/metro-config');
+const path = require('path');
+
+const themePackagePath = path.resolve(__dirname, '../react-native-dt-theme');
+const appNodeModules = path.resolve(__dirname, 'node_modules');
const config = getDefaultConfig(__dirname);
+// Watch the external theme package
+config.watchFolders = [themePackagePath];
+
+// Redirect all imports to use app's node_modules (avoids duplicates)
+config.resolver.extraNodeModules = {
+ 'react': path.resolve(appNodeModules, 'react'),
+ 'react-native': path.resolve(appNodeModules, 'react-native'),
+ 'react-native-paper': path.resolve(appNodeModules, 'react-native-paper'),
+ 'react-native-svg': path.resolve(appNodeModules, 'react-native-svg'),
+ 'react-native-safe-area-context': path.resolve(appNodeModules, 'react-native-safe-area-context'),
+};
+
+// Block theme package's node_modules entirely
+config.resolver.blockList = [
+ new RegExp(`${themePackagePath.replace(/[/\\]/g, '[/\\\\]')}/node_modules/.*`),
+];
+
module.exports = config;
diff --git a/package-lock.json b/package-lock.json
index e6590ab..bf873ae 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,10 +16,12 @@
"expo-status-bar": "^3.0.9",
"react": "19.1.0",
"react-native": "0.81.5",
+ "react-native-dt-theme": "file:../react-native-dt-theme",
"react-native-nfc-manager": "^3.17.2",
"react-native-paper": "^5.14.5",
"react-native-safe-area-context": "~5.6.0",
"react-native-screens": "~4.16.0",
+ "react-native-svg": "15.12.1",
"react-native-vector-icons": "^10.2.0"
},
"devDependencies": {
@@ -43,6 +45,31 @@
"node": ">=20"
}
},
+ "../react-native-dt-theme": {
+ "version": "0.1.0",
+ "license": "MIT",
+ "devDependencies": {
+ "@types/react": "^18.2.0",
+ "@types/react-native": "^0.72.0",
+ "react-native-paper": "^5.14.5",
+ "react-native-safe-area-context": "^5.6.2",
+ "react-native-svg": "^15.0.0",
+ "typescript": "^5.0.0"
+ },
+ "peerDependencies": {
+ "expo-font": ">=11.0.0",
+ "react": ">=18.0.0",
+ "react-native": ">=0.72.0",
+ "react-native-paper": ">=5.0.0",
+ "react-native-safe-area-context": ">=4.0.0",
+ "react-native-svg": ">=13.0.0"
+ },
+ "peerDependenciesMeta": {
+ "expo-font": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@0no-co/graphql.web": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.2.0.tgz",
@@ -4860,6 +4887,12 @@
"node": ">=0.6"
}
},
+ "node_modules/boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+ "license": "ISC"
+ },
"node_modules/bplist-creator": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz",
@@ -5430,6 +5463,47 @@
"node": ">=8"
}
},
+ "node_modules/css-select": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
+ "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.1.0",
+ "domhandler": "^5.0.2",
+ "domutils": "^3.0.1",
+ "nth-check": "^2.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/css-tree": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
+ "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
+ "license": "MIT",
+ "dependencies": {
+ "mdn-data": "2.0.14",
+ "source-map": "^0.6.1"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/css-what": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz",
+ "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
"node_modules/csstype": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
@@ -5688,6 +5762,61 @@
"node": ">=6.0.0"
}
},
+ "node_modules/dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "domelementtype": "^2.3.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
+ "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
"node_modules/dotenv": {
"version": "16.4.7",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
@@ -5770,6 +5899,18 @@
"node": ">= 0.8"
}
},
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
"node_modules/env-editor": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/env-editor/-/env-editor-0.4.2.tgz",
@@ -9873,6 +10014,12 @@
"node": ">= 0.4"
}
},
+ "node_modules/mdn-data": {
+ "version": "2.0.14",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
+ "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
+ "license": "CC0-1.0"
+ },
"node_modules/memoize-one": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
@@ -10467,6 +10614,18 @@
"node": ">=8"
}
},
+ "node_modules/nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/nth-check?sponsor=1"
+ }
+ },
"node_modules/nullthrows": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz",
@@ -11452,6 +11611,10 @@
}
}
},
+ "node_modules/react-native-dt-theme": {
+ "resolved": "../react-native-dt-theme",
+ "link": true
+ },
"node_modules/react-native-is-edge-to-edge": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.2.1.tgz",
@@ -11546,6 +11709,21 @@
"react-native": "*"
}
},
+ "node_modules/react-native-svg": {
+ "version": "15.12.1",
+ "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.12.1.tgz",
+ "integrity": "sha512-vCuZJDf8a5aNC2dlMovEv4Z0jjEUET53lm/iILFnFewa15b4atjVxU6Wirm6O9y6dEsdjDZVD7Q3QM4T1wlI8g==",
+ "license": "MIT",
+ "dependencies": {
+ "css-select": "^5.1.0",
+ "css-tree": "^1.1.3",
+ "warn-once": "0.1.1"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
"node_modules/react-native-vector-icons": {
"version": "10.3.0",
"resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-10.3.0.tgz",
diff --git a/package.json b/package.json
index 7991fc9..149ba7c 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
+ "react-native-dt-theme": "file:../react-native-dt-theme",
"@react-navigation/native": "^7.1.27",
"@react-navigation/native-stack": "^7.9.1",
"babel-preset-expo": "~54.0.10",
@@ -27,6 +28,7 @@
"react-native-paper": "^5.14.5",
"react-native-safe-area-context": "~5.6.0",
"react-native-screens": "~4.16.0",
+ "react-native-svg": "15.12.1",
"react-native-vector-icons": "^10.2.0"
},
"devDependencies": {
diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx
index 52873f5..82f2c9a 100644
--- a/src/screens/HomeScreen.tsx
+++ b/src/screens/HomeScreen.tsx
@@ -1,8 +1,8 @@
import React from 'react';
import {StyleSheet, View} from 'react-native';
-import {Button, Text, Surface} from 'react-native-paper';
+import {Text, Surface} from 'react-native-paper';
+import {DTButton, DTColors} from 'react-native-dt-theme';
import type {HomeScreenProps} from '../types/navigation';
-import {DTColors} from '../theme';
export function HomeScreen({navigation}: HomeScreenProps) {
return (
@@ -21,14 +21,11 @@ export function HomeScreen({navigation}: HomeScreenProps) {
Scan any NFC transponder to find compatible Dangerous Things implants.
-
+
@@ -74,21 +71,6 @@ const styles = StyleSheet.create({
opacity: 0.9,
paddingHorizontal: 20,
},
- scanButton: {
- borderColor: DTColors.modeNormal,
- borderWidth: 2,
- borderRadius: 4,
- },
- scanButtonLabel: {
- color: DTColors.modeNormal,
- fontSize: 18,
- fontWeight: '600',
- letterSpacing: 2,
- },
- scanButtonContent: {
- paddingVertical: 12,
- paddingHorizontal: 32,
- },
footer: {
alignItems: 'center',
paddingBottom: 20,
diff --git a/src/screens/ResultScreen.tsx b/src/screens/ResultScreen.tsx
index 19e5d99..19742e5 100644
--- a/src/screens/ResultScreen.tsx
+++ b/src/screens/ResultScreen.tsx
@@ -1,8 +1,8 @@
import React, { useMemo, useState } from 'react';
import { StyleSheet, View, ScrollView, Linking, TouchableOpacity } from 'react-native';
import { Button, Text, Surface, Divider, Chip } from 'react-native-paper';
+import { DTCard, DTButton, DTColors } from 'react-native-dt-theme';
import type { ResultScreenProps } from '../types/navigation';
-import { DTColors } from '../theme';
import { matchChipToProducts, getMatchSummary, getDesfireEvMismatchWarning, getMifareClassicCapacityWarning } from '../services/matching';
import { Product } from '../types/products';
import { getChipInfo, getChipFamilyInfo, getSecurityLevelDescription } from '../data/chipInfo';
@@ -72,11 +72,7 @@ export function ResultScreen({ route, navigation }: ResultScreenProps) {
{/* Chip Identification Card */}
{transponder && (
-
-
- CHIP IDENTIFIED
-
-
+
{transponder.chipName}
@@ -147,7 +143,7 @@ export function ResultScreen({ route, navigation }: ResultScreenProps) {
{transponder.cloneabilityNote}
)}
-
+
)}
{/* Educational Chip Info Card */}
@@ -380,11 +376,7 @@ export function ResultScreen({ route, navigation }: ResultScreenProps) {
)}
{/* Raw Tag Data Card */}
-
-
- RAW TAG DATA
-
-
+
{tagData ? (
<>
@@ -444,7 +436,7 @@ export function ResultScreen({ route, navigation }: ResultScreenProps) {
No tag data available
)}
-
+
{/* No Detection Card (fallback) */}
{!transponder && (
@@ -494,13 +486,12 @@ export function ResultScreen({ route, navigation }: ResultScreenProps) {
{/* Action Buttons */}
-
+
@@ -230,13 +229,11 @@ export function ScanScreen({ navigation }: ScanScreenProps) {
{getErrorHint()}
)}
-
+
TRY AGAIN
-
+
>
)}
@@ -245,13 +242,11 @@ export function ScanScreen({ navigation }: ScanScreenProps) {
READY TO SCAN
-
+
START
-
+
>
)}
@@ -368,32 +363,14 @@ const styles = StyleSheet.create({
paddingHorizontal: 20,
fontStyle: 'italic',
},
- retryButton: {
- borderColor: DTColors.modeNormal,
- borderWidth: 2,
- },
settingsButton: {
- borderColor: DTColors.modeEmphasis,
- borderWidth: 2,
marginTop: 16,
},
- settingsButtonLabel: {
- color: DTColors.modeEmphasis,
- letterSpacing: 2,
- },
readyText: {
color: DTColors.modeEmphasis,
letterSpacing: 2,
marginBottom: 32,
},
- startButton: {
- borderColor: DTColors.modeNormal,
- borderWidth: 2,
- },
- buttonLabel: {
- color: DTColors.modeNormal,
- letterSpacing: 2,
- },
processingText: {
color: DTColors.modeEmphasis,
letterSpacing: 4,
diff --git a/tsconfig.json b/tsconfig.json
index ca05969..3d5d033 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -3,7 +3,7 @@
"strict": true,
"target": "ESNext",
"lib": [
- "ES2020",
+ "ES2021",
"DOM"
],
"module": "ESNext",
@@ -21,13 +21,16 @@
]
},
"include": [
- "**/*.ts",
- "**/*.tsx"
+ "src/**/*.ts",
+ "src/**/*.tsx",
+ "*.ts",
+ "*.tsx"
],
"exclude": [
"node_modules",
"android",
- "ios"
+ "ios",
+ "../react-native-dt-theme"
],
"extends": "expo/tsconfig.base"
}