diff --git a/app.config.ts b/app.config.ts index de6c12ee..336550d1 100644 --- a/app.config.ts +++ b/app.config.ts @@ -2,7 +2,7 @@ import "tsx/cjs"; import { ConfigContext, ExpoConfig } from "expo/config"; -import constants from "@/libs/constants"; +import AppConfig from "@/config"; import buildPropertiesPlugin from "expo-build-properties/plugin"; import devBuildPlugin from "expo-dev-client/plugin"; import localAuthenticationPlugin from "expo-local-authentication/plugin"; @@ -12,6 +12,7 @@ import sharingPlugin from "expo-sharing/plugin"; import splashScreenPlugin from "expo-splash-screen/plugin"; import sqlitePlugin from "expo-sqlite/plugin"; import statusBarPlugin from "expo-status-bar/plugin"; +import withProtectUserDataPlugin from "./plugins/withProtectUserData/plugin"; const IS_DEV_BUILD = process.env.APP_VARIANT === "development"; const IS_PREVIEW_BUILD = process.env.APP_VARIANT === "preview"; @@ -69,9 +70,9 @@ if (!IS_PRODUCTION_BUILD) { export default ({ config }: ConfigContext): ExpoConfig => ({ ...config, - name: constants.appname + appNameSuffix, + name: AppConfig.APP_NAME + appNameSuffix, slug: "passcodes", - version: constants.build.version + versionNameSuffix, + version: AppConfig.BUILD_VERSION + versionNameSuffix, orientation: "default", icon: launcherAppIcon, @@ -81,8 +82,8 @@ export default ({ config }: ConfigContext): ExpoConfig => ({ platforms: ["android", "ios"], ios: { - buildNumber: constants.build.versionCodeIos, - version: constants.build.version, + buildNumber: AppConfig.BUILD_IOS_VERSIONCODE, + version: AppConfig.BUILD_VERSION, icon: launcherAppIcon, bundleIdentifier: "com.jeeldobariya.passcodes.earlybeta" + packageNameSuffix, @@ -93,8 +94,8 @@ export default ({ config }: ConfigContext): ExpoConfig => ({ }, android: { - versionCode: constants.build.versionCodeAndroid, - version: constants.build.version + versionNameSuffix, + versionCode: AppConfig.BUILD_ANDROID_VERSIONCODE, + version: AppConfig.BUILD_VERSION, package: "com.jeeldobariya.passcodes" + packageNameSuffix, icon: launcherAppIcon, adaptiveIcon: { @@ -145,8 +146,9 @@ export default ({ config }: ConfigContext): ExpoConfig => ({ expoScreenOrientationPlugin({ initialOrientation: "PORTRAIT", }), + withProtectUserDataPlugin({ allowBackup: false }), // TODO: we make it to allow backup when we have encryption. + "@react-native-vector-icons/fontawesome6", - "./plugins/withProtectUserData.ts", ], experiments: { typedRoutes: true, diff --git a/assets/images/android-icon-background.png b/assets/images/android-icon-background.png deleted file mode 100644 index ddcc8353..00000000 Binary files a/assets/images/android-icon-background.png and /dev/null differ diff --git a/assets/images/android-icon-foreground.png b/assets/images/android-icon-foreground.png deleted file mode 100644 index 9f802d1a..00000000 Binary files a/assets/images/android-icon-foreground.png and /dev/null differ diff --git a/assets/images/android-icon-monochrome.png b/assets/images/android-icon-monochrome.png deleted file mode 100644 index b484d83c..00000000 Binary files a/assets/images/android-icon-monochrome.png and /dev/null differ diff --git a/package-lock.json b/package-lock.json index de3cfaee..f0756023 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,29 +8,30 @@ "name": "passcodes", "version": "1.0.0", "dependencies": { + "@passcodes/passalgo": "github:PasscodesApp/PassAlgo#main", "@react-native-community/slider": "5.2.0", "@react-native-vector-icons/fontawesome6": "^13.1.2", "@shopify/flash-list": "2.0.2", "babel-plugin-inline-import": "^3.0.0", "drizzle-orm": "^0.45.2", - "expo": "~57.0.18", - "expo-build-properties": "~57.0.15", - "expo-clipboard": "~57.0.1", + "expo": "~57.0.22", + "expo-build-properties": "~57.0.17", + "expo-clipboard": "~57.0.2", "expo-constants": "~57.0.3", - "expo-dev-client": "~57.0.16", + "expo-dev-client": "~57.0.19", "expo-file-system": "~57.0.0", - "expo-linear-gradient": "~57.0.1", - "expo-linking": "~57.0.8", - "expo-local-authentication": "~57.0.2", + "expo-linear-gradient": "~57.0.2", + "expo-linking": "~57.0.10", + "expo-local-authentication": "~57.0.3", "expo-navigation-bar": "~57.0.2", - "expo-router": "~57.0.17", - "expo-screen-capture": "~57.0.2", + "expo-router": "~57.0.21", + "expo-screen-capture": "~57.0.3", "expo-screen-orientation": "~57.0.2", - "expo-sharing": "~57.0.16", - "expo-splash-screen": "~57.0.8", - "expo-sqlite": "~57.0.2", + "expo-sharing": "~57.0.19", + "expo-splash-screen": "~57.0.9", + "expo-sqlite": "~57.0.3", "expo-status-bar": "~57.0.1", - "expo-system-ui": "~57.0.3", + "expo-system-ui": "~57.0.4", "react": "19.2.3", "react-dom": "19.2.3", "react-native": "0.86.3", @@ -2233,9 +2234,9 @@ } }, "node_modules/@expo-google-fonts/material-symbols": { - "version": "0.4.43", - "resolved": "https://registry.npmjs.org/@expo-google-fonts/material-symbols/-/material-symbols-0.4.43.tgz", - "integrity": "sha512-jJzgVcbkJnl48X5iBFNaL5CQHwoBElJapl8unc2Ri8DnyuBR+gRiuAD4gu3ESZjjo3sknIoYIwloOsWzKbLknQ==", + "version": "0.4.47", + "resolved": "https://registry.npmjs.org/@expo-google-fonts/material-symbols/-/material-symbols-0.4.47.tgz", + "integrity": "sha512-5G4ObZlyGvDzx6g/+NC+RAMeeCc3dc0/HLu/llY5dYZUlNDYzaZWQZwPUzoTixf27riT8I7ur9mQKPYCPFDJzQ==", "license": "MIT AND Apache-2.0" }, "node_modules/@expo/code-signing-certificates": { @@ -2388,9 +2389,9 @@ "license": "MIT" }, "node_modules/@expo/fingerprint": { - "version": "0.20.11", - "resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.20.11.tgz", - "integrity": "sha512-GC43EcjQwzpzNZISqmhvjKdi+6FTfxnC9ZxOeg6osCkYXJz94j107l0QpPLipg62khD5HAA5G5cUhevffbRC4w==", + "version": "0.20.13", + "resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.20.13.tgz", + "integrity": "sha512-h1x+w+JpNRu88a/5SMZRzJs9YDGR/f7R8ApCr7OdenhqmJ00hFxQMzvxajDLL+1P2Y2g/ND0xksusqvBqWSW5Q==", "license": "MIT", "dependencies": { "@expo/env": "^2.4.3", @@ -2592,9 +2593,9 @@ } }, "node_modules/@expo/metro-file-map": { - "version": "57.0.2", - "resolved": "https://registry.npmjs.org/@expo/metro-file-map/-/metro-file-map-57.0.2.tgz", - "integrity": "sha512-tb50nSIWwKpRufSkGuivOK0FbUxv1Uwqptb0SzFTk0bkmYmcy3gIwnCbOHTfmrQDVndRhPEiu2SYgh9Z0PCvGg==", + "version": "57.0.3", + "resolved": "https://registry.npmjs.org/@expo/metro-file-map/-/metro-file-map-57.0.3.tgz", + "integrity": "sha512-1OXy+uPYY5uc7Tm4VBsd2NRn+3wHhqeqNuEO/Xo4kmYgv8FjYgUAc+bUXON9FpC2ikcLn4EVlGM9ce2exx9Mlg==", "license": "MIT", "dependencies": { "debug": "^4.3.4", @@ -2606,9 +2607,9 @@ } }, "node_modules/@expo/metro-runtime": { - "version": "57.0.14", - "resolved": "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-57.0.14.tgz", - "integrity": "sha512-LVpsfF0QOzlLSKlHYpxgW1cP1NKcYeaoalLO18jYHgqseSveEkb9VOffFpicTQ7AN03odZ4fgkUF+p5YpoY4yw==", + "version": "57.0.15", + "resolved": "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-57.0.15.tgz", + "integrity": "sha512-9RaFkqIzRud6ol9fc5ZKHgNDYeDjQVBdAQC+rjSQ6RiKV0pY+SjDqGmKY5a4M9TaV0qLMCNNrp5R88BqC2xbuQ==", "license": "MIT", "dependencies": { "@expo/log-box": "^57.0.4", @@ -3020,9 +3021,9 @@ } }, "node_modules/@expo/prebuild-config": { - "version": "57.0.15", - "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-57.0.15.tgz", - "integrity": "sha512-xTbWHroj0PDmlbqvmU+zF9ZZxveJkiuyiPoeRJYRGruFHebRAWnoTdw5S7d/UCzDBI8ropGGu9g2eb2nMxtvAw==", + "version": "57.0.16", + "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-57.0.16.tgz", + "integrity": "sha512-QA8oMZ7y70kjOzw+abhRl9uYC8Nu/abeUTHr0YVxb3pgIC14gc7w543bYtI+5gEDI3q55jKUViIauC76QvZN1w==", "license": "MIT", "dependencies": { "@expo/config": "~57.0.9", @@ -3032,7 +3033,7 @@ "@expo/json-file": "^11.0.1", "@react-native/normalize-colors": "0.86.3", "debug": "^4.3.1", - "expo-modules-autolinking": "~57.0.12", + "expo-modules-autolinking": "~57.0.13", "resolve-from": "^5.0.0", "semver": "^7.6.0" } @@ -3112,9 +3113,9 @@ "license": "MIT" }, "node_modules/@expo/ui": { - "version": "57.0.14", - "resolved": "https://registry.npmjs.org/@expo/ui/-/ui-57.0.14.tgz", - "integrity": "sha512-m8Z5dCplkOOQ70lu/xfB67qhk00Muw4U1T9frHBIW3LxxJeJZQ+jOmV6vCk6O6zVgoG/ib1et7PeX6etxI4Rsw==", + "version": "57.0.18", + "resolved": "https://registry.npmjs.org/@expo/ui/-/ui-57.0.18.tgz", + "integrity": "sha512-9RkA26v0FSyHlkQCyavZrT8EzsALfkNHIFO5uC+IsAxzWzItdOWdy6asjpg1yaCvBEGMYkP17FUZxluhVbeleA==", "license": "MIT", "dependencies": { "sf-symbols-typescript": "^2.1.0", @@ -3141,9 +3142,9 @@ } }, "node_modules/@expo/xcpretty": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/@expo/xcpretty/-/xcpretty-4.4.4.tgz", - "integrity": "sha512-4aQzz9vgxcNXFfo/iyNgDDYfsU5XGKKxWxZopw0cVotHiW+U8IJbIxMaxsINs6bHhtkG3StKNPcOrn3eBuxKPw==", + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/@expo/xcpretty/-/xcpretty-4.4.5.tgz", + "integrity": "sha512-J3eL4n4h5QTwfD0SIz8OIk6/+sOL/hFZAMacgCM07UNlxBQfJipEpIC2AQxvGkbYeStByJ0TVhQAJo+DeNgaSQ==", "license": "BSD-3-Clause", "dependencies": { "@babel/code-frame": "^7.20.0", @@ -3345,6 +3346,11 @@ "node": ">=12.4.0" } }, + "node_modules/@passcodes/passalgo": { + "version": "0.1.0", + "resolved": "git+ssh://git@github.com/PasscodesApp/PassAlgo.git#0b19e9c7324bc49fc189ba362588badf4c350546", + "license": "MIT" + }, "node_modules/@radix-ui/primitive": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.7.tgz", @@ -3407,43 +3413,6 @@ } } }, - "node_modules/@radix-ui/react-dialog": { - "version": "1.1.23", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.23.tgz", - "integrity": "sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-context": "1.2.2", - "@radix-ui/react-dismissable-layer": "1.1.19", - "@radix-ui/react-focus-guards": "1.1.6", - "@radix-ui/react-focus-scope": "1.1.16", - "@radix-ui/react-id": "1.1.4", - "@radix-ui/react-portal": "1.1.17", - "@radix-ui/react-presence": "1.1.10", - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-slot": "1.3.3", - "@radix-ui/react-use-controllable-state": "1.2.6", - "@radix-ui/react-use-layout-effect": "1.1.4", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.7.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-direction": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.4.tgz", @@ -3459,33 +3428,6 @@ } } }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.19.tgz", - "integrity": "sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-use-callback-ref": "1.1.4", - "@radix-ui/react-use-effect-event": "0.0.5" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-focus-guards": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.6.tgz", @@ -3501,31 +3443,6 @@ } } }, - "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.16.tgz", - "integrity": "sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-use-callback-ref": "1.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-id": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.4.tgz", @@ -3544,30 +3461,6 @@ } } }, - "node_modules/@radix-ui/react-portal": { - "version": "1.1.17", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.17.tgz", - "integrity": "sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-use-layout-effect": "1.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-presence": { "version": "1.1.10", "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.10.tgz", @@ -5066,9 +4959,9 @@ } }, "node_modules/agent-cli-detector": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/agent-cli-detector/-/agent-cli-detector-0.1.6.tgz", - "integrity": "sha512-vKrPeEVN3upDF3GjWxsWBbwQgMtNJ8VB1cduPvK3svmmz4ENpS7yaPxbogsbe3w+xp9xp2Cu+0Ar41rjAR4+lA==", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/agent-cli-detector/-/agent-cli-detector-0.1.7.tgz", + "integrity": "sha512-d8OWDVdZMgjhLUT9ZPgSv/BdFFF9pVuscC0JdUSz3bjwE15gcp6u/o0/JooM2yyAWC49KThFhXlgTXRa9B7yng==", "license": "MIT", "bin": { "agent-cli-detector": "dist/cli.js" @@ -5477,9 +5370,9 @@ } }, "node_modules/babel-preset-expo": { - "version": "57.0.9", - "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-57.0.9.tgz", - "integrity": "sha512-T19biGOBTnMp161PyBqWOoSIEeZug8/EjtuUdVob8JPQKZMTalHUQaQt+qtQQE7XsEkZJ6erb3k0CohneTFzQg==", + "version": "57.0.11", + "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-57.0.11.tgz", + "integrity": "sha512-R0NouDI3nzQUsjBh5TUJSDXLmuVzE2bp2YE0ywJkKxzrdWVjShpAlZMBuDeOQy6iKGB6ZCCvMqbQvpqgNMJ7iw==", "license": "MIT", "dependencies": { "@babel/generator": "^7.20.5", @@ -5528,7 +5421,7 @@ "peerDependencies": { "@babel/runtime": "^7.20.0", "expo": "*", - "expo-widgets": "^57.0.13", + "expo-widgets": "^57.0.18", "react-refresh": ">=0.14.0 <1.0.0" }, "peerDependenciesMeta": { @@ -7321,31 +7214,31 @@ } }, "node_modules/expo": { - "version": "57.0.18", - "resolved": "https://registry.npmjs.org/expo/-/expo-57.0.18.tgz", - "integrity": "sha512-6nax9hJPhf9dWrstliXUABTJXDNIJrfJKcCtjSxuYVs2Yf127GIhKf3wsEYSFUl+LFdRbPPTNaweJePH159wZA==", + "version": "57.0.22", + "resolved": "https://registry.npmjs.org/expo/-/expo-57.0.22.tgz", + "integrity": "sha512-YWUGqWtchsRQzcbU+Iqqt/1kFbZ9Pp3/eg+UenINahNt2RIaDElFJFFWLD1Yfex6WnBm6ESSE8h0/ja28hRqZg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.20.0", - "@expo/cli": "^57.0.20", + "@expo/cli": "^57.0.24", "@expo/config": "~57.0.9", "@expo/config-plugins": "~57.0.9", "@expo/devtools": "~57.0.1", "@expo/dom-webview": "~57.0.1", - "@expo/fingerprint": "^0.20.11", + "@expo/fingerprint": "^0.20.13", "@expo/local-build-cache-provider": "^57.0.8", "@expo/log-box": "^57.0.4", "@expo/metro": "~56.0.2", "@expo/metro-config": "~57.0.12", "@ungap/structured-clone": "^1.3.0", - "babel-preset-expo": "~57.0.9", - "expo-asset": "~57.0.15", - "expo-constants": "~57.0.16", - "expo-file-system": "~57.0.6", - "expo-font": "~57.0.2", - "expo-keep-awake": "~57.0.1", - "expo-modules-autolinking": "~57.0.12", - "expo-modules-core": "~57.0.14", + "babel-preset-expo": "~57.0.11", + "expo-asset": "~57.0.17", + "expo-constants": "~57.0.18", + "expo-file-system": "~57.0.7", + "expo-font": "~57.0.4", + "expo-keep-awake": "~57.0.2", + "expo-modules-autolinking": "~57.0.13", + "expo-modules-core": "~57.0.18", "pretty-format": "^29.7.0", "react-refresh": "^0.14.2", "whatwg-url-minimum": "^0.1.2" @@ -7383,13 +7276,13 @@ } }, "node_modules/expo-asset": { - "version": "57.0.15", - "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-57.0.15.tgz", - "integrity": "sha512-ZoiUftQb1Nn1jvL6l1kZoUXvgRCx57tMQYxSsp+g4ZvY+jgVWp/THruXOtPtZVdC/WWZxPUTct53bfAm2EPDAQ==", + "version": "57.0.17", + "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-57.0.17.tgz", + "integrity": "sha512-qSdWZBSfEg06o3GJMwcj36tw0vXkB6eYjqkQistWmyJkHxhUOKnsKJnev6OraXxLdoI+KOd1+qgp/ENx8gcqYA==", "license": "MIT", "dependencies": { "@expo/image-utils": "^0.11.5", - "expo-constants": "~57.0.15" + "expo-constants": "~57.0.18" }, "peerDependencies": { "expo": "*", @@ -7425,9 +7318,9 @@ } }, "node_modules/expo-build-properties": { - "version": "57.0.15", - "resolved": "https://registry.npmjs.org/expo-build-properties/-/expo-build-properties-57.0.15.tgz", - "integrity": "sha512-qZe9pnxlBpHT7SSNDR2a3C+P34xv67Ep9I0Erh+3xCczGlokyRd4pu9h0PrBdsVlr1d0QREQ1Do/+IgrIWkQQA==", + "version": "57.0.17", + "resolved": "https://registry.npmjs.org/expo-build-properties/-/expo-build-properties-57.0.17.tgz", + "integrity": "sha512-+qA5we2h1Gpnd24RGCR1amfSx7Mtf8glZ0jA5bbe1Nfdw9NdAIl+HPUqU7xyOUDKD0JWsHh9ap6yJG/jTs0tvA==", "license": "MIT", "dependencies": { "@expo/schema-utils": "^57.0.2", @@ -7451,9 +7344,9 @@ } }, "node_modules/expo-clipboard": { - "version": "57.0.1", - "resolved": "https://registry.npmjs.org/expo-clipboard/-/expo-clipboard-57.0.1.tgz", - "integrity": "sha512-HWICri4+1ao7S6QEfcorxVumXDiDnx1guGGewjZgGJWLGxFYs0RgH8ujBs+lkTzBkMmlwADaWSlaesR+nDJt5Q==", + "version": "57.0.2", + "resolved": "https://registry.npmjs.org/expo-clipboard/-/expo-clipboard-57.0.2.tgz", + "integrity": "sha512-VB4Au8X/RbvJKUtJk+87vdtveWSNEa5RzY3ooeO6VNF7Rd49RbQXZOu3/TfEW9p7mU8TCfyTvjyCiAUwSoSJ4w==", "license": "MIT", "peerDependencies": { "expo": "*", @@ -7462,9 +7355,9 @@ } }, "node_modules/expo-constants": { - "version": "57.0.16", - "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-57.0.16.tgz", - "integrity": "sha512-HG3yJjGZo2BPTo2F1sBoxdJZRwX7d9C554EhKn8m12K62pxGolCsvPSoh/lQf95OEP9vO34wwXB2C3QFs0KG0g==", + "version": "57.0.18", + "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-57.0.18.tgz", + "integrity": "sha512-cLDwDOniPfuCZPHNY++cAY2ZkK97u5aobbBvilPK2LLmaWxR4ZpD31LWUIzUDLrwMPxLjmh1uURnbeYEf0Mivw==", "license": "MIT", "dependencies": { "@expo/env": "~2.4.3" @@ -7475,30 +7368,30 @@ } }, "node_modules/expo-dev-client": { - "version": "57.0.16", - "resolved": "https://registry.npmjs.org/expo-dev-client/-/expo-dev-client-57.0.16.tgz", - "integrity": "sha512-Txss9sgzEFI0E+4I34E76vPiukeVDHnqCj0ZXy8SDijVM31/P6MCNniVbEZk7MBPQclD0aopfuaCFtkV8+r1sw==", + "version": "57.0.19", + "resolved": "https://registry.npmjs.org/expo-dev-client/-/expo-dev-client-57.0.19.tgz", + "integrity": "sha512-7giMK6BLL685GECHem2UAQvS473ImZLi5l+Q5tq+XSyv6I9UNVPs0p07mKwgBzq8z7DR3d4nTl702ziFxTjO3Q==", "license": "MIT", "dependencies": { - "expo-dev-launcher": "~57.0.16", - "expo-dev-menu": "~57.0.16", + "expo-dev-launcher": "~57.0.20", + "expo-dev-menu": "~57.0.18", "expo-dev-menu-interface": "~57.0.0", - "expo-manifests": "~57.0.1", - "expo-updates-interface": "~57.0.1" + "expo-manifests": "~57.0.2", + "expo-updates-interface": "~57.0.2" }, "peerDependencies": { "expo": "*" } }, "node_modules/expo-dev-launcher": { - "version": "57.0.16", - "resolved": "https://registry.npmjs.org/expo-dev-launcher/-/expo-dev-launcher-57.0.16.tgz", - "integrity": "sha512-jaBr6q4A5js/r465m2mRxN8A87YOi8I6PiIt8XIt4qfQWDgw6hqGovaF1AVFGT1Z5jxkjtgK8WNz/rAeypjqPQ==", + "version": "57.0.20", + "resolved": "https://registry.npmjs.org/expo-dev-launcher/-/expo-dev-launcher-57.0.20.tgz", + "integrity": "sha512-F6oAHaDn9j3L/gyMGE+OalriX6PHZWnZLxpN5jc6crTrp8f6hSwGhmsHpAvdOzijfMjyi3R2UkcISFsCIIQYFA==", "license": "MIT", "dependencies": { "@expo/schema-utils": "^57.0.2", - "expo-dev-menu": "~57.0.16", - "expo-manifests": "~57.0.1" + "expo-dev-menu": "~57.0.18", + "expo-manifests": "~57.0.2" }, "peerDependencies": { "expo": "*", @@ -7506,9 +7399,9 @@ } }, "node_modules/expo-dev-menu": { - "version": "57.0.16", - "resolved": "https://registry.npmjs.org/expo-dev-menu/-/expo-dev-menu-57.0.16.tgz", - "integrity": "sha512-KzMHtAmnr0GqyU6MCrKLzIo/AfOjcToGhzK+pFeXlcqSPOrPJbTGlZqQ3E1o2G2jZMs+M1XvXh/VgnRyJUaRGw==", + "version": "57.0.18", + "resolved": "https://registry.npmjs.org/expo-dev-menu/-/expo-dev-menu-57.0.18.tgz", + "integrity": "sha512-H7m9ENtVYz4FpJktQj+z/njIN1t7kOEaKCv8Qgv2SdAlyFTW8s2EnufxPoSlg2zUTanaauJ6wrR6XESHwW7kpw==", "license": "MIT", "dependencies": { "expo-dev-menu-interface": "~57.0.0" @@ -7541,9 +7434,9 @@ } }, "node_modules/expo-file-system": { - "version": "57.0.6", - "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-57.0.6.tgz", - "integrity": "sha512-pm8PMYEW6BnVOCBJ7df9FcDmQtE1tqImuYphlfYe1ipQRLYtdCayRczJcbKIsnB3mqEyp4gC2gWmMbsAsAOjVg==", + "version": "57.0.7", + "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-57.0.7.tgz", + "integrity": "sha512-1vkZRadOsIputFDBcclqBa1pO3iXOO6iIsdqJipQWw4V5TSnWgmRTuOF7ro8IzrVue+PHbum++KCG3Tawem4cg==", "license": "MIT", "peerDependencies": { "expo": "*", @@ -7551,9 +7444,9 @@ } }, "node_modules/expo-font": { - "version": "57.0.2", - "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-57.0.2.tgz", - "integrity": "sha512-eWR0CAdysgVqg8VewJ8b5wKh1pa6C8YShIEU3SP0+hlSqOk4djUwuJjn3P04KHVzL3hws9FrUuPxCNmd4TGung==", + "version": "57.0.4", + "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-57.0.4.tgz", + "integrity": "sha512-7WOMC2oA6xCfOm8bbofqWn2bIqyWFm7YLEnpc/gA1MJH0Ll0TfT/3+CqRkoW2eNRWMS8sxrz5WukUmSvmNquHA==", "license": "MIT", "dependencies": { "fontfaceobserver": "^2.1.0" @@ -7565,9 +7458,9 @@ } }, "node_modules/expo-glass-effect": { - "version": "57.0.1", - "resolved": "https://registry.npmjs.org/expo-glass-effect/-/expo-glass-effect-57.0.1.tgz", - "integrity": "sha512-m/n8maxqNcHk6ZDhuqXBfD5Kt1Iz3M8xykVgdB0iSCIXvF70IqWXmQhX8Psswhrp8eZ+3r0mAD0Jh/2gFA3QaA==", + "version": "57.0.3", + "resolved": "https://registry.npmjs.org/expo-glass-effect/-/expo-glass-effect-57.0.3.tgz", + "integrity": "sha512-aTh2J5eCxf+rSRMBJTnP8Ib5/k8DYFVDuEX1xqesN9F9aqTXocwMv/aQRVBNUs/iogDWhHIJz2pN97IsrrTcoA==", "license": "MIT", "peerDependencies": { "expo": "*", @@ -7576,15 +7469,15 @@ } }, "node_modules/expo-json-utils": { - "version": "57.0.1", - "resolved": "https://registry.npmjs.org/expo-json-utils/-/expo-json-utils-57.0.1.tgz", - "integrity": "sha512-cgTe1NqzQdYs/WN+3nIY5IZg8s0pb0xaTUbhYvxQDn137GbwRfHoGM2se3m3Vsl4Qu+B9G4RPEK5WJDEU2Do7g==", + "version": "57.0.2", + "resolved": "https://registry.npmjs.org/expo-json-utils/-/expo-json-utils-57.0.2.tgz", + "integrity": "sha512-9kYZECLKE3pYZQs+kt3+ggY83Lfryog9/UEo3HJKGcHUZeKKtlFBIPFjI4LzREubsvjP7AsvG0FdEX9EquIoAQ==", "license": "MIT" }, "node_modules/expo-keep-awake": { - "version": "57.0.1", - "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-57.0.1.tgz", - "integrity": "sha512-28lkFImeXTS+bhAjuCFV7w7tW5bXg27BJVrxv+nC/nyYa86qEa0oFeHwqol6ha5k4pdVDQgBF09GM4A1k76Ssg==", + "version": "57.0.2", + "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-57.0.2.tgz", + "integrity": "sha512-GqgH746wtJImmsbyHmCQoi7cOslKuWQBbJHXr53S35Bpf5UvxCAztvnitUEd7D0QvFN2rvkOhylbZpgZZfBLlQ==", "license": "MIT", "peerDependencies": { "expo": "*", @@ -7592,9 +7485,9 @@ } }, "node_modules/expo-linear-gradient": { - "version": "57.0.1", - "resolved": "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-57.0.1.tgz", - "integrity": "sha512-CpS8eMqoIWcHVGKV66zbDvzotCw9qYp3f8CuI9N+h1LaO0tMLUzBpkhAKePUsXlpN3yolYlHFSPkfVZ/uSh+iA==", + "version": "57.0.2", + "resolved": "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-57.0.2.tgz", + "integrity": "sha512-eGNSfcbdXYvw0B/TQ3nmXKQgj0jLUi3EgsXGlvI79rlXg5RR5dQTkY2yqkf7BQDM3PMd3tqBMZdsLhvBqSUaMg==", "license": "MIT", "peerDependencies": { "expo": "*", @@ -7603,12 +7496,12 @@ } }, "node_modules/expo-linking": { - "version": "57.0.8", - "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-57.0.8.tgz", - "integrity": "sha512-gv0ZNaF7tILbcvX8xinNbQr6GIGQStxeUAJseweP+LjC9ped7Wl6EeyIMBlDR83U6Da+8drVeqBQSMvwAXgytA==", + "version": "57.0.10", + "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-57.0.10.tgz", + "integrity": "sha512-RzRi4l4pIYzjgoCJKnqkO+lUI+M2YX6qgcMIQkIbLAXdcWrykBoKYwHGlmAkdFHQm89GaYcq329yfxG+c1hv8w==", "license": "MIT", "dependencies": { - "expo-constants": "~57.0.15", + "expo-constants": "~57.0.18", "invariant": "^2.2.4" }, "peerDependencies": { @@ -7617,9 +7510,9 @@ } }, "node_modules/expo-local-authentication": { - "version": "57.0.2", - "resolved": "https://registry.npmjs.org/expo-local-authentication/-/expo-local-authentication-57.0.2.tgz", - "integrity": "sha512-8K4zcrQ5wZkRS1rwEWY8qHbeGVMNh35e9D1VTVKlMHz1O47itW+pW6iBVuqwGFLozN4fRBeEDQH8hu9Gt58YuQ==", + "version": "57.0.3", + "resolved": "https://registry.npmjs.org/expo-local-authentication/-/expo-local-authentication-57.0.3.tgz", + "integrity": "sha512-1oQCBKz8JOpai+Zp+Z2RkBFIi2CHHWLSJk/pjvT9VEyf8nPFslo4kt1ZlQvtJ4rupbKNCmxqKfYr8Mlo8nJEug==", "license": "MIT", "dependencies": { "invariant": "^2.2.4" @@ -7629,21 +7522,21 @@ } }, "node_modules/expo-manifests": { - "version": "57.0.1", - "resolved": "https://registry.npmjs.org/expo-manifests/-/expo-manifests-57.0.1.tgz", - "integrity": "sha512-qB/mDG2dYdl+EvUeQuqP8KFYCFgFCQjJYdWIHo8SFBgDzMYmdF286DFY2M1M9Okr99wkb5M4tgA3aCcwv3aEQA==", + "version": "57.0.2", + "resolved": "https://registry.npmjs.org/expo-manifests/-/expo-manifests-57.0.2.tgz", + "integrity": "sha512-pPIIcKR73OShwlWMg8H90WRzKUd2krDdKlKAwuQV1FtUwE7H3hQEFw0ePFQY9yFoGMF/77ryYwNKfI5umMcCog==", "license": "MIT", "dependencies": { - "expo-json-utils": "~57.0.1" + "expo-json-utils": "~57.0.2" }, "peerDependencies": { "expo": "*" } }, "node_modules/expo-modules-autolinking": { - "version": "57.0.12", - "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-57.0.12.tgz", - "integrity": "sha512-Q8KAlq37nLKsQ+HsS9NpQVpd5jCgqtu694TDUNHBUBpV9ViD82mRBh8Uug/h68RG9xnLS+kuL4nYaCuFRghHjg==", + "version": "57.0.13", + "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-57.0.13.tgz", + "integrity": "sha512-Hj5NjZRlfccazhKab+wK19leRVUr1Y/PUERaRvegIbUSkq4GVxKq6lU9QHlKQyC16MdD/VS4iwsiLivB4o7lvQ==", "license": "MIT", "dependencies": { "@expo/require-utils": "^57.0.5", @@ -7656,13 +7549,13 @@ } }, "node_modules/expo-modules-core": { - "version": "57.0.14", - "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-57.0.14.tgz", - "integrity": "sha512-Cg3aQnVsQhZcMOF/RFgPGzMuhIak5W9eR9ecwmFSeQ813r4/49ut6RMA6PqUuUKuPCUIRqnBAHfSyTm1W9nfzg==", + "version": "57.0.18", + "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-57.0.18.tgz", + "integrity": "sha512-ovB7C+GVQTlKAOIsOIHJVVXN9Mn0ljf2SYBS+afrRaGqq+HZEn1D3o+HReIVeH1NjCn/xDBb7I4HjldHs6luZA==", "license": "MIT", "dependencies": { "@expo/expo-modules-macros-plugin": "0.6.1", - "expo-modules-jsi": "~57.0.6", + "expo-modules-jsi": "~57.1.0", "invariant": "^2.2.4" }, "peerDependencies": { @@ -7677,9 +7570,9 @@ } }, "node_modules/expo-modules-jsi": { - "version": "57.0.6", - "resolved": "https://registry.npmjs.org/expo-modules-jsi/-/expo-modules-jsi-57.0.6.tgz", - "integrity": "sha512-WK0xFEe0FdZTCLCdfXK+HVNYfAmEbA6goecRxqjr0gh3XYRKAr9tikxqcM0ItLQoEPKntscPZhWcPLGvr97Tfw==", + "version": "57.1.0", + "resolved": "https://registry.npmjs.org/expo-modules-jsi/-/expo-modules-jsi-57.1.0.tgz", + "integrity": "sha512-a5ckeHfnbYfonhcHGkM0EU/a0Keh+/OufXy/HyFS+spr5Ib0N4Oh1ZsSYFQhp5xdOUFkVofKRhfH+VmcccIIpA==", "license": "MIT", "peerDependencies": { "react-native": "*" @@ -7700,15 +7593,15 @@ } }, "node_modules/expo-router": { - "version": "57.0.17", - "resolved": "https://registry.npmjs.org/expo-router/-/expo-router-57.0.17.tgz", - "integrity": "sha512-zKAfGLxrbdkUFzzW1xHezLXPSa0V4DlYKEWweDUUFGgnPWPNy3OY0EKYAqOhlBGSDebAvtnJXn+2zyyJjYKwyg==", + "version": "57.0.21", + "resolved": "https://registry.npmjs.org/expo-router/-/expo-router-57.0.21.tgz", + "integrity": "sha512-r7UDLZU0gtVm8FW4nv2pxSl9TDhKlf2shcBbfp1T9STXJ3xYl9llLH701qQkICuXqqt3vGpOZ57GXC1ioVWg4g==", "license": "MIT", "dependencies": { "@expo/log-box": "^57.0.4", - "@expo/metro-runtime": "^57.0.14", + "@expo/metro-runtime": "^57.0.15", "@expo/schema-utils": "^57.0.2", - "@expo/ui": "^57.0.14", + "@expo/ui": "^57.0.18", "@radix-ui/react-slot": "^1.2.0", "@radix-ui/react-tabs": "^1.1.12", "@react-native-masked-view/masked-view": "^0.3.2", @@ -7716,12 +7609,12 @@ "color": "^4.2.3", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", - "expo-glass-effect": "^57.0.1", + "expo-glass-effect": "^57.0.3", "expo-server": "^57.0.3", - "expo-symbols": "^57.0.2", + "expo-symbols": "^57.0.3", "fast-deep-equal": "^3.1.3", "invariant": "^2.2.4", - "nanoid": "^3.3.8", + "nanoid": "^3.3.18", "query-string": "^7.1.3", "react-fast-compare": "^3.2.2", "react-is": "^19.1.0", @@ -7735,11 +7628,11 @@ }, "peerDependencies": { "@expo/log-box": "^57.0.4", - "@expo/metro-runtime": "^57.0.14", + "@expo/metro-runtime": "^57.0.15", "@testing-library/react-native": ">= 13.2.0", "expo": "*", - "expo-constants": "^57.0.15", - "expo-linking": "^57.0.8", + "expo-constants": "^57.0.18", + "expo-linking": "^57.0.10", "react": "*", "react-dom": "*", "react-native": "*", @@ -7772,9 +7665,9 @@ } }, "node_modules/expo-screen-capture": { - "version": "57.0.2", - "resolved": "https://registry.npmjs.org/expo-screen-capture/-/expo-screen-capture-57.0.2.tgz", - "integrity": "sha512-Ww8LFdSXuW7YeHc6/DwZd5tOEUqHAsOy4hLrfBxu99pbv+14sD9Lh0ueZeutg4QVktI+u2XJqFjMqyJ0b/5yxA==", + "version": "57.0.3", + "resolved": "https://registry.npmjs.org/expo-screen-capture/-/expo-screen-capture-57.0.3.tgz", + "integrity": "sha512-EpTd6tBgX2nWKJUvYDIrnsC2xWGJnlOLqN48EzLwPTQlbbwDUeWwt+4RDZOyFkWCa67XiUbrzIEOJApNBVZSow==", "license": "MIT", "peerDependencies": { "expo": "*", @@ -7801,9 +7694,9 @@ } }, "node_modules/expo-sharing": { - "version": "57.0.16", - "resolved": "https://registry.npmjs.org/expo-sharing/-/expo-sharing-57.0.16.tgz", - "integrity": "sha512-Z4ZFYLP8+EqIdUAiERYRI4/r7rtZDCRQ8Lj6jDG0xPF19B8nsFhYMgh7uyic+WulFEr+Xn7FeWgSDAuufkOyzw==", + "version": "57.0.19", + "resolved": "https://registry.npmjs.org/expo-sharing/-/expo-sharing-57.0.19.tgz", + "integrity": "sha512-lZ9chd/kxalZkMaJnPxW6dEpj7hnlrikesiEV9KLS99VKgkfLgH+/pFvIfk0YwGJ1b1/eLGcZ7OfJ2jjSgFpNA==", "license": "MIT", "dependencies": { "@expo/config-plugins": "^57.0.9", @@ -7817,9 +7710,9 @@ } }, "node_modules/expo-splash-screen": { - "version": "57.0.8", - "resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-57.0.8.tgz", - "integrity": "sha512-BEsrKg4niYBZa5AFzWRyGqxA4ZemtBOPwbCy+C336iGYYPuZYLEgWJGmm2xD1gRfCdrM/TP+l2ONd86Zvk3KyA==", + "version": "57.0.9", + "resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-57.0.9.tgz", + "integrity": "sha512-7cKDn7uBwg0RkcmY015/5j9st4msWJ4zQiPmQdVZoppW3cFSbv05n7xhM6CjUU466Tx4VRTqDF21HuIHkmnEtA==", "license": "MIT", "dependencies": { "@expo/config-plugins": "~57.0.9", @@ -7831,9 +7724,9 @@ } }, "node_modules/expo-sqlite": { - "version": "57.0.2", - "resolved": "https://registry.npmjs.org/expo-sqlite/-/expo-sqlite-57.0.2.tgz", - "integrity": "sha512-5KVbT7BQFZlIcQBXKWvwBERK3ODF6PSqct27GQKukZzAjsx2B97R+mwtYqrTUtUbw71VJrQrdr9Yl2OWYC7UpA==", + "version": "57.0.3", + "resolved": "https://registry.npmjs.org/expo-sqlite/-/expo-sqlite-57.0.3.tgz", + "integrity": "sha512-gbu9Nwm76DNpSK+MEhCjjjF3/EMyFE96IEDSlfO9jPiF4q48a7bef5EXnWVrwW8rvqCZKYAfSGqSpYNhhhpgIg==", "license": "MIT", "dependencies": { "await-lock": "^2.2.2" @@ -7856,9 +7749,9 @@ } }, "node_modules/expo-symbols": { - "version": "57.0.2", - "resolved": "https://registry.npmjs.org/expo-symbols/-/expo-symbols-57.0.2.tgz", - "integrity": "sha512-qZ0iqOflm5lZGwRsQ5Y8sDksw3GAUKwHSX1bJBoocXf7gu14vafIXXYWte+JT9VfXAUGyKodJhLHP/GoOrcNWg==", + "version": "57.0.3", + "resolved": "https://registry.npmjs.org/expo-symbols/-/expo-symbols-57.0.3.tgz", + "integrity": "sha512-uuK5yHLVR7kExUCrO/ItBvp/kp1maQ9Nkb6KvNy5ZUQnN0NMKwuhJ4GvbfS9N3ACmvCjTT4gKvg4yH5UuKIYNA==", "license": "MIT", "dependencies": { "@expo-google-fonts/material-symbols": "^0.4.1", @@ -7872,9 +7765,9 @@ } }, "node_modules/expo-system-ui": { - "version": "57.0.3", - "resolved": "https://registry.npmjs.org/expo-system-ui/-/expo-system-ui-57.0.3.tgz", - "integrity": "sha512-3N3BoagGojEvQw0h7kL0Lvv2LrVjqEvODSNS+Nc9lj/kzaTaVx4K1HI8qqxeALMf5jui3rLP2b3Ai2pXotoNuA==", + "version": "57.0.4", + "resolved": "https://registry.npmjs.org/expo-system-ui/-/expo-system-ui-57.0.4.tgz", + "integrity": "sha512-z//rxQJzhTNA1PcKrUtWmrd21jAS3nEDu1b0n8B/7LTTR933OcsZYP1HnbaZifuo2ZH/h7nce2Ow61WZ1Ig2bg==", "license": "MIT", "dependencies": { "@react-native/normalize-colors": "0.86.3", @@ -7892,18 +7785,18 @@ } }, "node_modules/expo-updates-interface": { - "version": "57.0.1", - "resolved": "https://registry.npmjs.org/expo-updates-interface/-/expo-updates-interface-57.0.1.tgz", - "integrity": "sha512-+LUWwJ0gf/TEKMVdQAw/Gjih4dvrk+URgy24X9qEGKuuMDZqjBRm9T4yQyBVALGL5TTdPUaB6ILxx3lshm3pwQ==", + "version": "57.0.2", + "resolved": "https://registry.npmjs.org/expo-updates-interface/-/expo-updates-interface-57.0.2.tgz", + "integrity": "sha512-x3SA5toiHZEkW+5oeNdUM1Rvt6M3ExegQsSwS3Z1+S9gn9utGYTGBkb8MBQivhjIZiRlQYDIhauxA8kySQEWtQ==", "license": "MIT", "peerDependencies": { "expo": "*" } }, "node_modules/expo/node_modules/@expo/cli": { - "version": "57.0.20", - "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-57.0.20.tgz", - "integrity": "sha512-ZjWz7SA5TBTyKq4+aFRUPgMFxmqHtKkDMv6d85J2UAAjdRhkNRf+B80t+rr5IFo2ywuTvyrPRBth4ei4w08olA==", + "version": "57.0.24", + "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-57.0.24.tgz", + "integrity": "sha512-CCBkvhPgAcrzJQ3daYbyOD6YuqKKkvOIyUfAbsUIvlpLomIlocYxFHYdmaC/KHqfCsbGFYSV/QKTSYshhSFNEA==", "license": "MIT", "dependencies": { "@expo/code-signing-certificates": "^0.0.6", @@ -7917,20 +7810,20 @@ "@expo/log-box": "^57.0.4", "@expo/metro": "~56.0.2", "@expo/metro-config": "~57.0.12", - "@expo/metro-file-map": "^57.0.2", + "@expo/metro-file-map": "^57.0.3", "@expo/osascript": "^2.7.1", "@expo/package-manager": "^1.13.1", "@expo/plist": "^0.8.1", - "@expo/prebuild-config": "^57.0.15", + "@expo/prebuild-config": "^57.0.16", "@expo/require-utils": "^57.0.5", - "@expo/router-server": "^57.0.8", + "@expo/router-server": "^57.0.9", "@expo/schema-utils": "^57.0.2", "@expo/spawn-async": "^1.8.0", "@expo/ws-tunnel": "^2.0.0", "@expo/xcpretty": "^4.4.4", "@react-native/dev-middleware": "0.86.3", "accepts": "^1.3.8", - "agent-cli-detector": "0.1.6", + "agent-cli-detector": "0.1.7", "arg": "^5.0.2", "bplist-creator": "0.1.0", "bplist-parser": "^0.3.1", @@ -7984,18 +7877,18 @@ } }, "node_modules/expo/node_modules/@expo/cli/node_modules/@expo/router-server": { - "version": "57.0.8", - "resolved": "https://registry.npmjs.org/@expo/router-server/-/router-server-57.0.8.tgz", - "integrity": "sha512-lEPGYoDmh97yyEcY/MFWQsfE7yIgMRnGDcnsBN7B6AiWBFSJIMEcGnO0aYPgY0ZjNWyJDi7Ee8QZRGqB+LhI6w==", + "version": "57.0.9", + "resolved": "https://registry.npmjs.org/@expo/router-server/-/router-server-57.0.9.tgz", + "integrity": "sha512-/PxRQozFesIyCJZOAtrQE8XcmcojNiL5ctPMQnbE4ojC2EJPu0zc7c0Y4PuXsoRxrZxm8Usw76/lCVrXcfTZ2w==", "license": "MIT", "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { - "@expo/metro-runtime": "^57.0.14", + "@expo/metro-runtime": "^57.0.15", "expo": "*", - "expo-constants": "^57.0.15", - "expo-font": "^57.0.1", + "expo-constants": "^57.0.17", + "expo-font": "^57.0.3", "expo-router": "*", "expo-server": "^57.0.3", "react": "*", @@ -11483,9 +11376,9 @@ } }, "node_modules/postcss": { - "version": "8.5.26", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", - "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", "funding": [ { "type": "opencollective", @@ -11502,7 +11395,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.17", + "nanoid": "^3.3.18", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -14158,6 +14051,119 @@ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" } }, + "node_modules/vaul/node_modules/@radix-ui/react-dialog": { + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.23.tgz", + "integrity": "sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/vaul/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.19.tgz", + "integrity": "sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-effect-event": "0.0.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/vaul/node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.16.tgz", + "integrity": "sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/vaul/node_modules/@radix-ui/react-portal": { + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.17.tgz", + "integrity": "sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/vlq": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz", diff --git a/package.json b/package.json index 44ba8743..97354ab3 100644 --- a/package.json +++ b/package.json @@ -18,29 +18,30 @@ "deploy:x86_64": "eas build --profile deploy-x86_64 --platform android --local" }, "dependencies": { + "@passcodes/passalgo": "github:PasscodesApp/PassAlgo#main", "@react-native-community/slider": "5.2.0", "@react-native-vector-icons/fontawesome6": "^13.1.2", "@shopify/flash-list": "2.0.2", "babel-plugin-inline-import": "^3.0.0", "drizzle-orm": "^0.45.2", - "expo": "~57.0.18", - "expo-build-properties": "~57.0.15", - "expo-clipboard": "~57.0.1", + "expo": "~57.0.22", + "expo-build-properties": "~57.0.17", + "expo-clipboard": "~57.0.2", "expo-constants": "~57.0.3", - "expo-dev-client": "~57.0.16", + "expo-dev-client": "~57.0.19", "expo-file-system": "~57.0.0", - "expo-linear-gradient": "~57.0.1", - "expo-linking": "~57.0.8", - "expo-local-authentication": "~57.0.2", + "expo-linear-gradient": "~57.0.2", + "expo-linking": "~57.0.10", + "expo-local-authentication": "~57.0.3", "expo-navigation-bar": "~57.0.2", - "expo-router": "~57.0.17", - "expo-screen-capture": "~57.0.2", + "expo-router": "~57.0.21", + "expo-screen-capture": "~57.0.3", "expo-screen-orientation": "~57.0.2", - "expo-sharing": "~57.0.16", - "expo-splash-screen": "~57.0.8", - "expo-sqlite": "~57.0.2", + "expo-sharing": "~57.0.19", + "expo-splash-screen": "~57.0.9", + "expo-sqlite": "~57.0.3", "expo-status-bar": "~57.0.1", - "expo-system-ui": "~57.0.3", + "expo-system-ui": "~57.0.4", "react": "19.2.3", "react-dom": "19.2.3", "react-native": "0.86.3", diff --git a/plugins/withProtectUserData.ts b/plugins/withProtectUserData/index.ts similarity index 52% rename from plugins/withProtectUserData.ts rename to plugins/withProtectUserData/index.ts index 62aa1536..4055823c 100644 --- a/plugins/withProtectUserData.ts +++ b/plugins/withProtectUserData/index.ts @@ -1,16 +1,22 @@ import { ConfigPlugin, withAndroidManifest } from "expo/config-plugins"; +import { ProtectUserDataProps } from "./plugin"; -const withProtectUserData: ConfigPlugin = (config) => { +const withProtectUserDataPlugin: ConfigPlugin = ( + config, + props, +) => { return withAndroidManifest(config, (config) => { const mainApplication = config?.modResults?.manifest?.application?.[0]; if (mainApplication) { mainApplication.$["android:hasFragileUserData"] = "true"; - mainApplication.$["android:allowBackup"] = "false"; + mainApplication.$["android:allowBackup"] = props.allowBackup + ? "true" + : "false"; } return config; }); }; -export default withProtectUserData; +export default withProtectUserDataPlugin; diff --git a/plugins/withProtectUserData/plugin.ts b/plugins/withProtectUserData/plugin.ts new file mode 100644 index 00000000..617efdb7 --- /dev/null +++ b/plugins/withProtectUserData/plugin.ts @@ -0,0 +1,10 @@ +export type ProtectUserDataProps = { + /** Whether to allow Android to back up the app's data. */ + allowBackup: boolean; +}; + +export default function withProtectUserDataPlugin( + props: ProtectUserDataProps = { allowBackup: false }, +): [string, ProtectUserDataProps] { + return ["@plugins/withProtectUserData", props]; +} diff --git a/src/app/(tabs)/(password-manager)/index.tsx b/src/app/(tabs)/(password-manager)/index.tsx index 970a82a3..a6f28a35 100644 --- a/src/app/(tabs)/(password-manager)/index.tsx +++ b/src/app/(tabs)/(password-manager)/index.tsx @@ -1,14 +1,12 @@ +import PasswordItemCard from "@/components/PasswordItemCard"; import Text from "@/components/Text"; -import { passwords } from "@/db/schema"; -import formatDate from "@/utils/formating"; +import { usePasswordRepository } from "@/contexts/RepositoryContext"; import FontAwesome6 from "@react-native-vector-icons/fontawesome6"; import { FlashList } from "@shopify/flash-list"; -import { eq } from "drizzle-orm"; -import { drizzle, useLiveQuery } from "drizzle-orm/expo-sqlite"; +import { useLiveQuery } from "drizzle-orm/expo-sqlite"; import { router, Stack } from "expo-router"; -import { useSQLiteContext } from "expo-sqlite"; import { useMemo, useState } from "react"; -import { Alert, Pressable, StyleSheet, View } from "react-native"; +import { Alert, Pressable } from "react-native"; import { FAB, IconButton, useTheme } from "react-native-paper"; export default function LoadPasswordScreen() { @@ -21,11 +19,10 @@ export default function LoadPasswordScreen() { const theme = useTheme(); - const db = useSQLiteContext(); - const drizzleDb = drizzle(db); + const passwordRepository = usePasswordRepository(); const { data: passwordList = [] } = useLiveQuery( - drizzleDb.select().from(passwords), + passwordRepository.observeAll(), [refreshKey], ); @@ -66,9 +63,7 @@ export default function LoadPasswordScreen() { { text: "Delete", onPress: async () => { - await drizzleDb - .delete(passwords) - .where(eq(passwords.id, password.id)); + await passwordRepository.delete(password.id); }, style: "destructive", }, @@ -128,7 +123,14 @@ export default function LoadPasswordScreen() { }} showsVerticalScrollIndicator={false} ListEmptyComponent={ - + {searchQuery.trim() ? "No passwords found!!" : "No data!!"} } @@ -143,44 +145,12 @@ export default function LoadPasswordScreen() { } onLongPress={() => deletePassword(item)} > - - - Domain:{" "} - - {item.domain} - - - - - Username:{" "} - - {item.username} - - - - - Password:{" "} - - ********** - - - - - Updated At:{" "} - - {item.updatedAt == null - ? "just now" - : formatDate(item.updatedAt)} - - - + /> )} /> @@ -206,21 +176,3 @@ export default function LoadPasswordScreen() { ); } - -const styles = StyleSheet.create({ - label: { - fontWeight: "600", - marginBottom: 6, - }, - - value: { - fontWeight: "400", - }, - - emptyText: { - textAlign: "center", - marginTop: 40, - fontSize: 12, - color: "gray", - }, -}); diff --git a/src/app/(tabs)/(password-manager)/password-details.tsx b/src/app/(tabs)/(password-manager)/password-details.tsx index 193be56a..8412c0f7 100644 --- a/src/app/(tabs)/(password-manager)/password-details.tsx +++ b/src/app/(tabs)/(password-manager)/password-details.tsx @@ -1,37 +1,29 @@ -import FormTextField from "@/components/FormTextField"; -import SecureTextField from "@/components/SecureTextField"; +import PasswordDetailCardActions from "@/components/PasswordDetailCardActions"; +import PasswordFormCard, { + usePasswordForm, +} from "@/components/PasswordFormCard"; +import { usePasswordRepository } from "@/contexts/RepositoryContext"; import { useToast } from "@/contexts/ToastContext"; -import { passwords } from "@/db/schema"; import { getScreenShotSecureScreen } from "@/libs/screenshot_prevention"; -import formatDate from "@/utils/formating"; +import { formatDate } from "@passcodes/passalgo"; import FontAwesome6 from "@react-native-vector-icons/fontawesome6"; -import { eq } from "drizzle-orm"; -import { drizzle } from "drizzle-orm/expo-sqlite"; import { Href, router, useLocalSearchParams } from "expo-router"; import { usePreventRemove } from "expo-router/react-navigation"; -import { useSQLiteContext } from "expo-sqlite"; import { useEffect, useState } from "react"; -import { Keyboard, ScrollView, TextInput, View } from "react-native"; +import { Keyboard, ScrollView, TextInput } from "react-native"; -import { Button, FAB } from "react-native-paper"; +import { FAB } from "react-native-paper"; import { SafeAreaView } from "react-native-safe-area-context"; export default function PasswordDetailsScreen() { const { id } = useLocalSearchParams(); - const db = useSQLiteContext(); - const drizzleDb = drizzle(db); - - const [domain, setDomain] = useState(""); - const [username, setUsername] = useState(""); - const [password, setPassword] = useState(""); - const [url, setUrl] = useState(""); - const [notes, setNotes] = useState(""); - const [lastupdateAt, setLastupdateAt] = useState(""); + const passwordRepository = usePasswordRepository(); + const { state, setState, updateField } = usePasswordForm(); const [isEditing, setIsEditing] = useState(false); const { showToast } = useToast(); @@ -52,21 +44,24 @@ export default function PasswordDetailsScreen() { } async function loadAndRefreshPassword() { - const result = await drizzleDb - .select() - .from(passwords) - .where(eq(passwords.id, Number(id))); - - if (result.length > 0) { - const data = result[0]; - - setDomain(data.domain || ""); - setUsername(data.username || ""); - setPassword(data.password || ""); - setUrl(data.url || ""); - setNotes(data.notes || ""); - setLastupdateAt(data.updatedAt ? formatDate(data.updatedAt) : "just now"); + const password = await passwordRepository.getById(Number(id)); + + if (!password) { + // Password doesn't exist + // TODO: redirect to error or go back(). + return; } + + setState({ + domain: password.domain, + username: password.username, + password: password.password, + url: password.url || "", + notes: password.notes || "", + updatedAt: password.updatedAt + ? formatDate(password.updatedAt) + : "just now", + }); } async function updatePassword() { @@ -79,17 +74,7 @@ export default function PasswordDetailsScreen() { unfocusAllFields(); try { - await drizzleDb - .update(passwords) - .set({ - domain, - username, - password, - url, - notes, - updatedAt: new Date().toISOString(), - }) - .where(eq(passwords.id, Number(id))); + await passwordRepository.update(Number(id), state); setIsEditing(false); @@ -142,92 +127,17 @@ export default function PasswordDetailsScreen() { }} keyboardShouldPersistTaps="handled" > - - - - - - - - - - router.navigate(state.url as Href)} + onCancel={handleCancel} /> - - - {!isEditing && ( - - )} - - {isEditing && ( - - )} - )} - onPress={() => { - if (!domain || !username || !password) { - Alert.alert( - "Missing Fields", - "Domain, Username and Password are required.", - ); - return; - } - - drizzleDb - .insert(passwords) - .values({ - domain, - username, - password, - notes, - url, - }) - .then(() => { - showToast("Password saved successfully"); - router.back(); - }) - .catch((err) => { - console.error(err); - showToast("Failed to save, please try again!!", "error"); - Alert.alert("Error", "Failed to save password."); - }); - }} + onPress={handleSavePassword} > Save Password diff --git a/src/app/(tabs)/(settings)/data-recovery.tsx b/src/app/(tabs)/(settings)/data-recovery.tsx index 4b980b55..ace9688a 100644 --- a/src/app/(tabs)/(settings)/data-recovery.tsx +++ b/src/app/(tabs)/(settings)/data-recovery.tsx @@ -1,7 +1,8 @@ import ScreenHeading from "@/components/ScreenHeading"; import Text from "@/components/Text"; +import Config from "@/config"; import dataRecoveryFromTestDB from "@/libs/data_recovery_test_db_mess"; -import * as SQLite from "expo-sqlite"; +import { withSQLiteDatabase } from "@/libs/withSQLiteDatabase"; import { useEffect, useState } from "react"; import { SafeAreaView } from "react-native-safe-area-context"; @@ -11,15 +12,16 @@ type MigrationResult = { }; export default function DataRecoveryScreen() { - const expoDb = SQLite.useSQLiteContext(); const [result, setResult] = useState({ state: "Running", - message: "please wait...", + message: "Please wait...", }); useEffect(() => { async function runMigration() { - await dataRecoveryFromTestDB(expoDb); + await withSQLiteDatabase(Config.DATABASE_NAME, async (expoDb) => { + await dataRecoveryFromTestDB(expoDb); + }); } runMigration() @@ -29,10 +31,10 @@ export default function DataRecoveryScreen() { message: "Done!! PROBLEM FIXED", }); }) - .catch((e) => { + .catch((error) => { setResult({ state: "Error", - message: e.toString(), + message: error instanceof Error ? error.message : String(error), }); }); }, []); @@ -50,7 +52,12 @@ export default function DataRecoveryScreen() { {result.state} - + + {result.message} diff --git a/src/app/(tabs)/(settings)/get-back-passwords.android.tsx b/src/app/(tabs)/(settings)/get-back-passwords.android.tsx index 22282b56..cfd6fe4d 100644 --- a/src/app/(tabs)/(settings)/get-back-passwords.android.tsx +++ b/src/app/(tabs)/(settings)/get-back-passwords.android.tsx @@ -1,7 +1,8 @@ import ScreenHeading from "@/components/ScreenHeading"; import Text from "@/components/Text"; +import Config from "@/config"; import roomDrizzleMigration from "@/libs/room_drizzle_migration"; -import * as SQLite from "expo-sqlite"; +import { withSQLiteDatabase } from "@/libs/withSQLiteDatabase"; import AsyncStorage from "expo-sqlite/kv-store"; import { useEffect, useState } from "react"; import { Button } from "react-native"; @@ -21,8 +22,6 @@ export default function GetBackPasswordsScreen() { isError: false, }); - const expoDb = SQLite.useSQLiteContext(); - useEffect(() => { async function runMigration() { try { @@ -50,7 +49,12 @@ export default function GetBackPasswordsScreen() { }); // Run migration - const result = await roomDrizzleMigration(expoDb); + const result = await withSQLiteDatabase( + Config.DATABASE_NAME, + async (expoDb) => { + return await roomDrizzleMigration(expoDb); + }, + ); await AsyncStorage.setItem(MIGRATION_KEY, MIGRATION_STATUS.SUCCESS); diff --git a/src/app/(tabs)/(settings)/settings.tsx b/src/app/(tabs)/(settings)/settings.tsx index fbe6283b..1053c637 100644 --- a/src/app/(tabs)/(settings)/settings.tsx +++ b/src/app/(tabs)/(settings)/settings.tsx @@ -1,13 +1,12 @@ import LinkButton from "@/components/LinkButton"; import { LinkIconButton } from "@/components/LinkIconButton"; import Text from "@/components/Text"; -import { passwords } from "@/db/schema"; +import Config from "@/config"; import { isBiometricsAuthEnabled, toggleBiometricsFeature, unlockWithBiometricsApp, } from "@/libs/biometric"; -import constants from "@/libs/constants"; import { getGooglePasswordsCSVContent, type PasswordCSVFormat, @@ -22,14 +21,13 @@ import { toggleScreenshotPreventionFeature, } from "@/libs/screenshot_prevention"; import { FontAwesome6 } from "@react-native-vector-icons/fontawesome6"; -import { drizzle } from "drizzle-orm/expo-sqlite"; import { LinearGradient } from "expo-linear-gradient"; -import { useSQLiteContext } from "expo-sqlite"; import { useState } from "react"; import { Alert, ScrollView } from "react-native"; import { Card, Divider, List, Switch, useTheme } from "react-native-paper"; import { SafeAreaView } from "react-native-safe-area-context"; +import { usePasswordRepository } from "@/contexts/RepositoryContext"; import PasscodesAutofillServiceModule from "../../../../modules/passcodes-autofill-service/src/PasscodesAutofillServiceModule"; export default function SettingsScreen() { @@ -45,38 +43,21 @@ export default function SettingsScreen() { const toggleScreenshotPreventSwitch = () => setIsScreenshotPreventEnabled(toggleScreenshotPreventionFeature()); - const [isAutofillEnabled, setIsAutofillEnabled] = useState( - PasscodesAutofillServiceModule.isAutofillServiceEnabled(), - ); + const isAutofillEnabled = + PasscodesAutofillServiceModule.isAutofillServiceEnabled(); const theme = useTheme(); - let db = useSQLiteContext(); - const drizzleDb = drizzle(db); + const passwordRepository = usePasswordRepository(); async function handleImportPasswords() { let content = await getCSVPasswordString(); let importPasswordList: PasswordCSVFormat[] = convertRawCSVToPasswords(content); - - drizzleDb.transaction((tx) => { - importPasswordList.forEach((importablePassword) => { - tx.insert(passwords) - .values({ - domain: importablePassword.domain, - username: importablePassword.username, - password: importablePassword.password, - notes: importablePassword.notes, - url: importablePassword.url, - }) - .execute(); - }); - }); + await passwordRepository.importAll(importPasswordList); } async function handleExportPasswords() { - const result: PasswordCSVFormat[] = await drizzleDb - .select() - .from(passwords); + const result: PasswordCSVFormat[] = await passwordRepository.getAll(); let content = await getGooglePasswordsCSVContent(result); sharePasswordAsCSV(content); @@ -307,10 +288,10 @@ export default function SettingsScreen() { ( ( ( ( + - + ); } diff --git a/src/components/PasswordDetailCardActions.tsx b/src/components/PasswordDetailCardActions.tsx new file mode 100644 index 00000000..2164b7d5 --- /dev/null +++ b/src/components/PasswordDetailCardActions.tsx @@ -0,0 +1,54 @@ +import FontAwesome6 from "@react-native-vector-icons/fontawesome6"; +import { View } from "react-native"; +import { Button } from "react-native-paper"; + +export default function PasswordDetailCardActions({ + isEditing, + onOpen, + onCancel, +}: { + isEditing: boolean; + onOpen: () => void; + onCancel: () => void; +}) { + return ( + + {!isEditing && ( + + )} + + {isEditing && ( + + )} + + ); +} diff --git a/src/components/PasswordFormCard.tsx b/src/components/PasswordFormCard.tsx new file mode 100644 index 00000000..c750c8c4 --- /dev/null +++ b/src/components/PasswordFormCard.tsx @@ -0,0 +1,112 @@ +import FormTextField from "@/components/FormTextField"; +import SecureTextField from "@/components/SecureTextField"; +import { useState } from "react"; + +import { View } from "react-native"; + +type PasswordFormValue = { + domain: string; + username: string; + password: string; + url: string; + notes: string; + updatedAt: string; +}; + +// xxxxx Component xxxxx + +type PasswordFormCardProps = { + value: PasswordFormValue; + editable: boolean; + + onChange: ( + field: K, + value: PasswordFormValue[K], + ) => void; +}; + +export default function PasswordFormCard({ + value, + editable, + onChange, +}: PasswordFormCardProps) { + return ( + + onChange("domain", text)} + editable={editable} + /> + + onChange("username", text)} + editable={editable} + autoCapitalize="none" + autoCorrect={false} + /> + + onChange("password", text)} + editable={editable} + /> + + onChange("url", text)} + editable={editable} + autoCapitalize="none" + autoCorrect={false} + /> + + onChange("notes", text)} + editable={editable} + multiline + numberOfLines={3} + /> + + + + ); +} + +// xxxxx Hook xxxxx + +export function usePasswordForm() { + const [state, setState] = useState({ + domain: "", + username: "", + password: "", + url: "", + notes: "", + updatedAt: "", + }); + + function updateField( + field: K, + value: PasswordFormValue[K], + ) { + setState((current) => ({ + ...current, + [field]: value, + })); + } + + return { + state, + setState, + updateField, + }; +} diff --git a/src/components/PasswordItemCard.tsx b/src/components/PasswordItemCard.tsx new file mode 100644 index 00000000..7d062d62 --- /dev/null +++ b/src/components/PasswordItemCard.tsx @@ -0,0 +1,72 @@ +import Text from "@/components/Text"; +import { formatDate } from "@passcodes/passalgo"; +import { StyleSheet, View, ViewStyle } from "react-native"; +import { useTheme } from "react-native-paper"; + +type Props = { + domain: string; + username: string; + updatedAt: string | null; + style?: ViewStyle; +}; + +export default function PasswordItemCard({ + domain, + username, + updatedAt, + ...props +}: Props) { + const theme = useTheme(); + + return ( + + + Domain:{" "} + + {domain} + + + + + Username:{" "} + + {username} + + + + + Password:{" "} + + ********** + + + + + Updated At:{" "} + + {updatedAt ? formatDate(updatedAt) : "just now"} + + + + ); +} + +const styles = StyleSheet.create({ + label: { + fontWeight: "600", + marginBottom: 6, + }, + + value: { + fontWeight: "400", + }, +}); diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 00000000..9b799d07 --- /dev/null +++ b/src/config.ts @@ -0,0 +1,33 @@ +export type SocialLinks = `https://${string}` | `mailto:${string}`; + +const config = { + // ---------------------- + // Main Config + // ---------------------- + APP_NAME: "Passcodes", + VERSION: "v4.0.0 - RC.1", + + // ---------------------- + // Build Config + // ---------------------- + BUILD_VERSION: "v4.0.0-RC.1", // same as VERSION, just without spaces. + BUILD_ANDROID_VERSIONCODE: 10, + BUILD_IOS_VERSIONCODE: "1.0.0", + + // ---------------------- + // Social Config + // ---------------------- + SOCIAL_EMAIL_US: "mailto:jeeldobariya38@gmail.com" as SocialLinks, + SOCIAL_GITHUB: "https://github.com/PasscodesApp/Passcodes" as SocialLinks, + SOCIAL_TELEGRAM: "https://t.me/passcodescommunity" as SocialLinks, + SOCIAL_DISCORD: "https://discord.gg/kSSkYq7KAQ" as SocialLinks, + + // ---------------------- + // Database Config + // ---------------------- + DATABASE_NAME: "master.db", + OLD_MISTAKEN_DATABASE_NAME: "test2.db", // This variable should never be editted in anycases... +}; + +export type AppConfig = typeof config; +export default config; diff --git a/src/contexts/RepositoryContext.tsx b/src/contexts/RepositoryContext.tsx new file mode 100644 index 00000000..a79c6e3b --- /dev/null +++ b/src/contexts/RepositoryContext.tsx @@ -0,0 +1,54 @@ +import { createContext, PropsWithChildren, useContext, useMemo } from "react"; + +import DrizzleDatabaseProvider, { useDrizzleDatabase } from "@/db/provider"; +import { PasswordRepository } from "@/repositories/PasswordRepository"; + +type Repositories = { + password: PasswordRepository; +}; + +const RepositoryContext = createContext(undefined); + +function RepositoryProviderContent({ + children, +}: { + children: React.ReactNode; +}) { + const db = useDrizzleDatabase(); + + const repositories = useMemo( + () => ({ + password: new PasswordRepository(db), + }), + [db], + ); + + return ( + + {children} + + ); +} + +export function RepositoryProvider({ + dbName, + children, +}: PropsWithChildren<{ dbName: string }>) { + return ( + + {children} + + ); +} + +export function usePasswordRepository(): PasswordRepository { + const repositories = useContext(RepositoryContext); + + if (!repositories) { + throw new Error( + "usePasswordRepository must be used inside RepositoryProvider", + ); + } + + return repositories.password; +} diff --git a/src/db/provider.tsx b/src/db/provider.tsx index d1810cb1..e423a6cd 100644 --- a/src/db/provider.tsx +++ b/src/db/provider.tsx @@ -1,76 +1,190 @@ import Text from "@/components/Text"; import { drizzle } from "drizzle-orm/expo-sqlite"; import { useMigrations } from "drizzle-orm/expo-sqlite/migrator"; -import { openDatabaseSync, SQLiteProvider } from "expo-sqlite"; -import { Suspense, type PropsWithChildren } from "react"; +import { openDatabaseSync } from "expo-sqlite"; +import { + createContext, + Suspense, + useContext, + type PropsWithChildren, +} from "react"; import { View } from "react-native"; import { ActivityIndicator } from "react-native-paper"; import { SafeAreaView } from "react-native-safe-area-context"; + import migrations from "./drizzle/migrations"; +import * as schema from "./schema"; + +export type DrizzleDatabase = ReturnType; + +const DrizzleDatabaseContext = createContext( + undefined, +); -export const DATABASE_NAME = "master.db"; +interface DrizzleDatabaseProviderProps extends PropsWithChildren { + /** + * Name of the SQLite database file. + */ + databaseName: string; +} -export default function DatabaseProvider({ children }: PropsWithChildren) { - const expoDb = openDatabaseSync(DATABASE_NAME); +/** + * Provides a single shared Drizzle database instance to the + * entire component tree. + * + * The provider owns the complete database lifecycle: + * + * 1. Opens the Expo SQLite database. + * 2. Creates the Drizzle database instance. + * 3. Runs Drizzle migrations. + * 4. Waits until migrations have completed successfully. + * 5. Provides the ready Drizzle database through React context. + * + * Children are never rendered before the database is ready. + * Migration failures are displayed using the provider's fatal + * database error UI. + * + * The database instance is shared by all descendants. Consumers + * should access it using `useDrizzleDatabase()` instead of opening + * their own SQLite connection. + * + * @example + * ```tsx + * + * + * + * ``` + */ +export default function DrizzleDatabaseProvider({ + children, + databaseName, +}: DrizzleDatabaseProviderProps) { + const expoDb = openDatabaseSync(databaseName, { + enableChangeListener: true, + }); - const db = drizzle(expoDb); + const db = drizzle(expoDb, { schema }); const { success, error } = useMigrations(db, migrations); if (error) { - return ( - ; + } + + if (!success) { + return ; + } + + return ( + + }>{children} + + ); +} + +/** + * Returns the shared Drizzle database. + * + * this hook is only intended to be use inside `DrizzleDatabaseProvider`. + */ +export function useDrizzleDatabase(): DrizzleDatabase { + const db = useContext(DrizzleDatabaseContext); + + if (db === undefined) { + throw new Error( + "useDrizzleDatabase must be used inside DrizzleDatabaseProvider.", + ); + } + + return db; +} + +function DrizzleDatabaseLoading() { + return ( + + + + Please Wait..... + Opening Local Database... + + ); +} + +function DrizzleDatabaseError({ error }: { error: unknown }) { + return ( + + - - Something went wrong!! - - - Please close & reopen the app. + + ! - - ); - } - if (!success) { - return ( - - - - ); - } + + Database initialization failed + - return ( - - - - } - > - - {children} - - + Passcodes couldn't prepare its local database. Please close and reopen + the app. + + + ); } diff --git a/src/db/schema.ts b/src/db/schema.ts index 7999cf4e..9d720fee 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -10,4 +10,17 @@ export const passwords = sqliteTable("passwords", { url: text("url"), createdAt: text("created_at").default(sql`(CURRENT_TIMESTAMP)`), updatedAt: text("updated_at").default(sql`(CURRENT_TIMESTAMP)`), + + /* + * Suggested Updates:- + * + * createdAt: text("created_at") + * .notNull() + * .default(sql`CURRENT_TIMESTAMP`), + * + * updatedAt: text("updated_at") + * .notNull() + * .default(sql`CURRENT_TIMESTAMP`) + * .$onUpdateFn(() => sql`CURRENT_TIMESTAMP`), + */ }); diff --git a/src/libs/constants.ts b/src/libs/constants.ts deleted file mode 100644 index 65ef859c..00000000 --- a/src/libs/constants.ts +++ /dev/null @@ -1,9 +0,0 @@ -export default { - appname: "Passcodes", - version: "v4.0.0 - RC.1", - build: { - version: "v4.0.0-RC.1", - versionCodeAndroid: 10, - versionCodeIos: "1.0.0", - }, -}; diff --git a/src/libs/data_recovery_test_db_mess.ts b/src/libs/data_recovery_test_db_mess.ts index 0009e292..a6432de9 100644 --- a/src/libs/data_recovery_test_db_mess.ts +++ b/src/libs/data_recovery_test_db_mess.ts @@ -1,14 +1,13 @@ +import Config from "@/config"; import * as SQLite from "expo-sqlite"; -const OLD_DATABASE_NAME = "test2.db"; - export default async function dataRecoveryFromTestDB( expoDb: SQLite.SQLiteDatabase, ) { let oldDb: SQLite.SQLiteDatabase | undefined = undefined; try { - oldDb = await SQLite.openDatabaseAsync(OLD_DATABASE_NAME); + oldDb = await SQLite.openDatabaseAsync(Config.OLD_MISTAKEN_DATABASE_NAME); // Verify passwords table exists const tableExists = await oldDb.getFirstAsync( @@ -20,7 +19,7 @@ export default async function dataRecoveryFromTestDB( if (!tableExists) { await oldDb.closeAsync(); - await SQLite.deleteDatabaseAsync(OLD_DATABASE_NAME); + await SQLite.deleteDatabaseAsync(Config.OLD_MISTAKEN_DATABASE_NAME); return; } diff --git a/src/libs/importing.ts b/src/libs/importing.ts index 60a1bd40..81cf8da9 100644 --- a/src/libs/importing.ts +++ b/src/libs/importing.ts @@ -29,7 +29,9 @@ export function convertRawCSVToPasswords( lines.shift(); return lines.map((line) => { - const [domain, url, username, password, notes = ""] = line.split(","); + // TODO: Assigning empty string is tempoary solution, we will improve this later. + const [domain = "", url = "", username = "", password = "", notes = ""] = + line.split(","); return { domain: domain, diff --git a/src/libs/withSQLiteDatabase.ts b/src/libs/withSQLiteDatabase.ts new file mode 100644 index 00000000..44bcbbea --- /dev/null +++ b/src/libs/withSQLiteDatabase.ts @@ -0,0 +1,28 @@ +import * as SQLite from "expo-sqlite"; + +/** + * Opens a SQLite database, passes the connection to the callback, + * returns the callback result, and closes the database afterward. + * + * The callback can return either a value or a Promise. + * + * @example + * ```ts + * const result = await withSQLiteDatabase( + * "master.db", + * (db) => query(db), + * ); + * ``` + */ +export async function withSQLiteDatabase( + databaseName: string, + callback: (db: SQLite.SQLiteDatabase) => T | Promise, +): Promise { + const db = await SQLite.openDatabaseAsync(databaseName); + + try { + return await callback(db); + } finally { + await db.closeAsync(); + } +} diff --git a/src/repositories/PasswordRepository.ts b/src/repositories/PasswordRepository.ts new file mode 100644 index 00000000..ea43a861 --- /dev/null +++ b/src/repositories/PasswordRepository.ts @@ -0,0 +1,94 @@ +import type { DrizzleDatabase } from "@/db/provider"; +import { passwords } from "@/db/schema"; +import { eq, sql } from "drizzle-orm"; + +export interface CreatePasswordInput { + domain: string; + username: string; + password: string; + notes?: string | null; + url?: string | null; +} + +export interface UpdatePasswordInput { + domain?: string; + username?: string; + password?: string; + notes?: string | null; + url?: string | null; +} + +export class PasswordRepository { + constructor(private readonly db: DrizzleDatabase) {} + + /** + * Creates a query for observing all passwords. + * + * This does not execute the query. Pass the returned query + * to a reactive consumer such as `useLiveQuery`. + */ + observeAll() { + return this.db.select().from(passwords); + } + + async getAll() { + return this.observeAll(); + } + + async getById(id: number) { + const [password] = await this.db + .select() + .from(passwords) + .where(eq(passwords.id, id)); + + return password; + } + + async create(data: CreatePasswordInput) { + const [createdPassword] = await this.db + .insert(passwords) + .values(data) + .returning(); + + return createdPassword; + } + + // TODO: temporary solution we will create a upsert all method + async importAll(data: CreatePasswordInput[]) { + this.db.transaction((tx) => { + data.forEach((importablePassword) => { + tx.insert(passwords) + .values({ + domain: importablePassword.domain, + username: importablePassword.username, + password: importablePassword.password, + notes: importablePassword.notes, + url: importablePassword.url, + }) + .execute(); + }); + }); + } + + async update(id: number, data: UpdatePasswordInput) { + const [updatedPassword] = await this.db + .update(passwords) + .set({ + ...data, + updatedAt: sql`CURRENT_TIMESTAMP`, // TODO: It will no longer be required, once drizzle `$onUpdate()` will be used. + }) + .where(eq(passwords.id, id)) + .returning(); + + return updatedPassword; + } + + async delete(id: number) { + const [deletedPassword] = await this.db + .delete(passwords) + .where(eq(passwords.id, id)) + .returning(); + + return deletedPassword; + } +} diff --git a/src/utils/formating.ts b/src/utils/formating.ts deleted file mode 100644 index 33617e62..00000000 --- a/src/utils/formating.ts +++ /dev/null @@ -1,20 +0,0 @@ -export default function formatDate(isoString: string): string { - const date = new Date(isoString); - - const months = [ - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "June", - "July", - "Augt", - "Sept", - "Oct", - "Nov", - "Dec", - ]; - - return `${months[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`; -} diff --git a/tsconfig.json b/tsconfig.json index ec96c84d..b96f4bd9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,8 +2,18 @@ "extends": "expo/tsconfig.base", "compilerOptions": { "strict": true, + + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "forceConsistentCasingInFileNames": true, + "paths": { - "@/*": ["./src/*"] + "@/*": ["./src/*"], + "@plugins/*": ["./plugins/*"] } }, "include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"]