diff --git a/touchgrass.app/.prettierrc b/touchgrass.app/.prettierrc
new file mode 100644
index 0000000..5af07a2
--- /dev/null
+++ b/touchgrass.app/.prettierrc
@@ -0,0 +1,6 @@
+{
+ "printWidth": 100,
+ "tabWidth": 2,
+ "singleQuote": false,
+ "bracketSameLine": true
+}
diff --git a/touchgrass.app/app.json b/touchgrass.app/app.json
index 4316ac8..a70abeb 100644
--- a/touchgrass.app/app.json
+++ b/touchgrass.app/app.json
@@ -31,7 +31,15 @@
"imageWidth": 76
}
],
- "expo-secure-store"
+ "expo-secure-store",
+ [
+ "expo-camera",
+ {
+ "cameraPermission": "Allow $(PRODUCT_NAME) to access your camera",
+ "barcodeScannerEnabled": true
+ }
+ ],
+ "@react-native-vector-icons/fontawesome6"
],
"experiments": {
"typedRoutes": true,
diff --git a/touchgrass.app/assets/oauth-client-metadata.json b/touchgrass.app/assets/oauth-client-metadata.json
index e0c5e0e..f04e3ed 100644
--- a/touchgrass.app/assets/oauth-client-metadata.json
+++ b/touchgrass.app/assets/oauth-client-metadata.json
@@ -1,19 +1,12 @@
{
- "client_id": "https://touchgrass.lunarequest.gay/oauth-client-metadata.json",
- "client_name": "Touch Grass",
- "client_uri": "https://touchgrass.lunarequest.gay",
- "redirect_uris": [
- "gay.lunarequest.touchgrass:/auth/callback"
- ],
- "scope": "atproto repo:gay.lunarequest.touchgrass.pubkey repo:gay.lunarequest.touchgrass.touchedgrass",
- "token_endpoint_auth_method": "none",
- "response_types": [
- "code"
- ],
- "grant_types": [
- "authorization_code",
- "refresh_token"
- ],
- "application_type": "native",
- "dpop_bound_access_tokens": true
-}
\ No newline at end of file
+ "client_id": "https://touchgrass.lunarequest.gay/oauth-client-metadata.json",
+ "client_name": "Touch Grass",
+ "client_uri": "https://touchgrass.lunarequest.gay",
+ "redirect_uris": ["gay.lunarequest.touchgrass:/auth/callback"],
+ "scope": "atproto repo:gay.lunarequest.touchgrass.pubkey repo:gay.lunarequest.touchgrass.touchedgrass",
+ "token_endpoint_auth_method": "none",
+ "response_types": ["code"],
+ "grant_types": ["authorization_code", "refresh_token"],
+ "application_type": "native",
+ "dpop_bound_access_tokens": true
+}
diff --git a/touchgrass.app/bun.lock b/touchgrass.app/bun.lock
index 1f56b80..22b3946 100644
--- a/touchgrass.app/bun.lock
+++ b/touchgrass.app/bun.lock
@@ -5,29 +5,31 @@
"": {
"name": "touchgrass.app",
"dependencies": {
- "@atcute/atproto": "^4.0.4",
- "@atcute/client": "^5.1.2",
- "@atcute/identity": "^2",
- "@atcute/identity-resolver": "^2",
"@atcute/lex-cli": "^3.3.0",
"@atcute/lexicons": "^2",
"@atproto/api": "^0.24.3",
+ "@atproto/common-web": "^0.5.14",
"@atproto/oauth-client-expo": "^0.2.6",
- "@expo/ui": "~57.0.22",
+ "@expo/ui": "~57.0.23",
+ "@noble/post-quantum": "^0.7.1",
+ "@react-native-async-storage/async-storage": "2.2.0",
+ "@react-native-vector-icons/fontawesome6": "^13.1.5",
"@react-native/typescript-config": "^0.87.1",
"@types/node": "^26.6.4",
"core-js": "^3.50.0",
- "expo": "^57.0.27",
+ "expo": "~57.0.28",
+ "expo-blur": "~57.0.3",
+ "expo-camera": "~57.0.6",
"expo-constants": "~57.0.21",
- "expo-dev-client": "~57.0.19",
+ "expo-dev-client": "~57.0.20",
"expo-device": "~57.0.2",
"expo-font": "~57.0.4",
"expo-glass-effect": "~57.0.4",
"expo-image": "~57.0.5",
"expo-linking": "~57.0.12",
- "expo-router": "~57.0.25",
+ "expo-router": "~57.0.26",
"expo-secure-store": "~57.0.4",
- "expo-splash-screen": "~57.0.9",
+ "expo-splash-screen": "~57.0.10",
"expo-status-bar": "~57.0.1",
"expo-symbols": "~57.0.3",
"expo-system-ui": "~57.0.4",
@@ -37,12 +39,17 @@
"react-dom": "19.2.3",
"react-native": "0.86.3",
"react-native-css": "3.1.0-rc.0",
+ "react-native-device-info": "^15.0.2",
"react-native-gesture-handler": "~2.32.0",
+ "react-native-get-random-values": "~1.11.0",
"react-native-mmkv": "^4.3.2",
"react-native-nitro-modules": "^0.37.1",
+ "react-native-nitro-totp": "^2.0.1",
+ "react-native-qrcode-svg": "^6.3.26",
"react-native-reanimated": "4.5.1",
"react-native-safe-area-context": "~5.7.0",
"react-native-screens": "~4.26.0",
+ "react-native-svg": "15.15.4",
"react-native-url-polyfill": "^4.0.0",
"react-native-web": "~0.21.0",
"react-native-worklets": "0.10.1",
@@ -54,8 +61,11 @@
"babel-preset-expo": "^57.0.14",
"eslint": "^9",
"eslint-config-expo": "~57.0.2",
+ "eslint-config-prettier": "^10.1.8",
+ "eslint-plugin-prettier": "^5.5.6",
"lightningcss": "1.30.1",
"postcss": "8.5.29",
+ "prettier": "^3.9.10",
"prettier-plugin-tailwindcss": "^0.5.11",
"tailwindcss": "4.1.12",
"typescript": "~6.0.3",
@@ -71,16 +81,12 @@
"packages": {
"@alloc/quick-lru": ["@alloc/quick-lru@5.3.0", "", {}, "sha512-U4+70Pc5ZS9osnCBCE5Jha/ciHM+Yp+CNMNC/7HvYbNRk1Ldd+f7qO65W5qfhu/TCv+/ozljlXXe9Nj8419DMA=="],
- "@atcute/atproto": ["@atcute/atproto@4.0.4", "", { "dependencies": { "@atcute/lexicons": "^2.0.3" } }, "sha512-aEp5wQVPVbZcFJUwoi54tyT+ehRI/xtPe9T0zIvcryHAVXF/zxHtNboM+htbZLOAGsDFsqzgCyxph0Bafw7L8w=="],
-
"@atcute/car": ["@atcute/car@6.1.0", "", { "dependencies": { "@atcute/cbor": "^2.3.8", "@atcute/cid": "^2.5.0", "@atcute/uint8array": "^1.2.0", "@atcute/varint": "^2.0.2" } }, "sha512-Dp7MlyI3YT7NiIUK6bm2ZyA95Pdun+DuFlcr40ZsGdqhOkKrctZT7wMZXSs6IYd7RorjSnddEJDh51hqrkNSPQ=="],
"@atcute/cbor": ["@atcute/cbor@2.3.8", "", { "dependencies": { "@atcute/cid": "^2.5.0", "@atcute/multibase": "^1.2.5", "@atcute/uint8array": "^1.2.0" } }, "sha512-gazXBcNTr2U3cRaCjfRbq2GKHJnWapUUA3mgehL+0tbVXEty2wf/LIwF9z46gjqjX2DYGGwkybF8lTI3dzHaNQ=="],
"@atcute/cid": ["@atcute/cid@2.5.0", "", { "dependencies": { "@atcute/multibase": "^1.2.5", "@atcute/uint8array": "^1.2.0" } }, "sha512-CAZyzUdaQhuC+S7tGjoC7vgyDI2X4JU+CPO/7WjEU7HlNsAGXjSkD++5TlfnSWGDqUeqpjMZjUfFBZYHKzPHew=="],
- "@atcute/client": ["@atcute/client@5.1.2", "", { "dependencies": { "@atcute/identity": "^2.0.2", "@atcute/lexicons": "^2.1.0" } }, "sha512-V3MOJudmmbuiFfrmLR3/ZGA+PKcI4E4ZNEDGKMX/YrvFrHyElD1/gb8NxLesrlIcHefiulqfX9dAl6hOkIt7CA=="],
-
"@atcute/crypto": ["@atcute/crypto@2.4.4", "", { "dependencies": { "@atcute/multibase": "^1.2.5", "@atcute/uint8array": "^1.1.5", "@noble/secp256k1": "^3.1.0" } }, "sha512-Yc7lXz4ndDjbs+/WrKeGS+sVEr+sx8xi5zSVInKNl0FAEY6KbvcT+bjaU9A3erUVviFp9yEcu/1aSg/f+GPpBg=="],
"@atcute/identity": ["@atcute/identity@2.0.2", "", { "dependencies": { "@atcute/lexicons": "^2.0.3", "valibot": "^1.4.2" } }, "sha512-amr/EQceqVtBVmjBK4uUF7nKKYuRttadigpvOcAn4dnO6SNSwSjQi8KDH9LnukEotPsSP4UDsQvNfHWEoUcslw=="],
@@ -325,7 +331,7 @@
"@expo-google-fonts/material-symbols": ["@expo-google-fonts/material-symbols@0.4.49", "", {}, "sha512-HtoaXYCk7A9kOjwoxrjq6i0iDtAoQnOPm31zUn+TgmKlHVUU+yyqvQDxN830MvGmBQV8eINv/SK7JRtWhB2xcg=="],
- "@expo/cli": ["@expo/cli@57.0.28", "", { "dependencies": { "@expo/code-signing-certificates": "^0.0.6", "@expo/config": "~57.0.10", "@expo/config-plugins": "~57.0.10", "@expo/devcert": "^1.2.1", "@expo/env": "~2.4.3", "@expo/image-utils": "^0.11.6", "@expo/inline-modules": "^0.1.7", "@expo/json-file": "^11.0.1", "@expo/log-box": "^57.0.4", "@expo/metro": "~56.0.2", "@expo/metro-config": "~57.0.13", "@expo/metro-file-map": "^57.0.4", "@expo/osascript": "^2.7.1", "@expo/package-manager": "^1.13.1", "@expo/plist": "^0.8.1", "@expo/prebuild-config": "^57.0.17", "@expo/require-utils": "^57.0.6", "@expo/router-server": "^57.0.12", "@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.7", "arg": "^5.0.2", "bplist-creator": "0.1.0", "bplist-parser": "^0.3.1", "chalk": "^4.0.0", "ci-info": "^3.3.0", "compression": "^1.7.4", "connect": "^3.7.0", "debug": "^4.3.4", "dnssd-advertise": "^1.1.4", "expo-server": "^57.0.3", "fetch-nodeshim": "^0.4.10", "getenv": "^2.0.0", "glob": "^13.0.0", "lan-network": "^0.2.1", "multitars": "^1.0.2", "node-forge": "^1.3.3", "npm-package-arg": "^11.0.0", "ora": "^3.4.0", "picomatch": "^4.0.4", "pretty-format": "^29.7.0", "progress": "^2.0.3", "prompts": "^2.3.2", "resolve-from": "^5.0.0", "sandbox-cli-detector": "^0.2.0", "semver": "^7.6.0", "send": "^0.19.0", "slugify": "^1.3.4", "stacktrace-parser": "^0.1.10", "structured-headers": "^0.4.1", "terminal-link": "^2.1.1", "toqr": "^0.1.1", "wrap-ansi": "^7.0.0", "ws": "^8.12.1", "zod": "^3.25.76" }, "peerDependencies": { "expo": "*", "expo-router": "*", "react-native": "*" }, "optionalPeers": ["expo-router", "react-native"], "bin": { "expo-internal": "main.js" } }, "sha512-4969gk/B2JyP3kAv2ftpwU1f9dLG2j9TsQuYCTTjfnyRx0O49Jwu1O1mie2c3HmKXbwfNA6QTS+3i3sNetMTag=="],
+ "@expo/cli": ["@expo/cli@57.0.29", "", { "dependencies": { "@expo/code-signing-certificates": "^0.0.6", "@expo/config": "~57.0.10", "@expo/config-plugins": "~57.0.10", "@expo/devcert": "^1.2.1", "@expo/env": "~2.4.3", "@expo/image-utils": "^0.11.6", "@expo/inline-modules": "^0.1.8", "@expo/json-file": "^11.0.1", "@expo/log-box": "^57.0.4", "@expo/metro": "~56.0.2", "@expo/metro-config": "~57.0.13", "@expo/metro-file-map": "^57.0.4", "@expo/osascript": "^2.7.1", "@expo/package-manager": "^1.13.1", "@expo/plist": "^0.8.1", "@expo/prebuild-config": "^57.0.18", "@expo/require-utils": "^57.0.6", "@expo/router-server": "^57.0.12", "@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.7", "arg": "^5.0.2", "bplist-creator": "0.1.0", "bplist-parser": "^0.3.1", "chalk": "^4.0.0", "ci-info": "^3.3.0", "compression": "^1.7.4", "connect": "^3.7.0", "debug": "^4.3.4", "dnssd-advertise": "^1.1.4", "expo-server": "^57.0.3", "fetch-nodeshim": "^0.4.10", "getenv": "^2.0.0", "glob": "^13.0.0", "lan-network": "^0.2.1", "multitars": "^1.0.2", "node-forge": "^1.3.3", "npm-package-arg": "^11.0.0", "ora": "^3.4.0", "picomatch": "^4.0.4", "pretty-format": "^29.7.0", "progress": "^2.0.3", "prompts": "^2.3.2", "resolve-from": "^5.0.0", "sandbox-cli-detector": "^0.2.0", "semver": "^7.6.0", "send": "^0.19.0", "slugify": "^1.3.4", "stacktrace-parser": "^0.1.10", "structured-headers": "^0.4.1", "terminal-link": "^2.1.1", "toqr": "^0.1.1", "wrap-ansi": "^7.0.0", "ws": "^8.12.1", "zod": "^3.25.76" }, "peerDependencies": { "expo": "*", "expo-router": "*", "react-native": "*" }, "optionalPeers": ["expo-router", "react-native"], "bin": { "expo-internal": "main.js" } }, "sha512-OBmtg+74j6dEEQjKTgRDjQBvLSkKyP2cDTuGIrJTdgG4gZ1HCEBZj18Kyk9UfdYRXgJdixSVAQMCZK6/Q6l2QQ=="],
"@expo/code-signing-certificates": ["@expo/code-signing-certificates@0.0.6", "", { "dependencies": { "node-forge": "^1.3.3" } }, "sha512-iNe0puxwBNEcuua9gmTGzq+SuMDa0iATai1FlFTMHJ/vUmKvN/V//drXoLJkVb5i5H3iE/n/qIJxyoBnXouD0w=="],
@@ -345,15 +351,15 @@
"@expo/expo-modules-macros-plugin": ["@expo/expo-modules-macros-plugin@0.6.1", "", {}, "sha512-cpsLZE4rqkc1Y3eZTkxB98jrqY1YXgetmtxFt8q89jBRmk3quRuk1BZo+VcnCSObZardjg99r1k5xijEMONFGA=="],
- "@expo/fingerprint": ["@expo/fingerprint@0.20.13", "", { "dependencies": { "@expo/env": "^2.4.3", "@expo/spawn-async": "^1.8.0", "arg": "^5.0.2", "chalk": "^4.1.2", "debug": "^4.3.4", "getenv": "^2.0.0", "glob": "^13.0.0", "ignore": "^5.3.1", "minimatch": "^10.2.2", "resolve-from": "^5.0.0", "semver": "^7.6.0" }, "bin": { "fingerprint": "bin/cli.js" } }, "sha512-h1x+w+JpNRu88a/5SMZRzJs9YDGR/f7R8ApCr7OdenhqmJ00hFxQMzvxajDLL+1P2Y2g/ND0xksusqvBqWSW5Q=="],
+ "@expo/fingerprint": ["@expo/fingerprint@0.20.14", "", { "dependencies": { "@expo/env": "^2.4.3", "@expo/spawn-async": "^1.8.0", "arg": "^5.0.2", "chalk": "^4.1.2", "debug": "^4.3.4", "getenv": "^2.0.0", "glob": "^13.0.0", "ignore": "^5.3.1", "minimatch": "^10.2.2", "resolve-from": "^5.0.0", "semver": "^7.6.0" }, "bin": { "fingerprint": "bin/cli.js" } }, "sha512-7sihStGX1ZepJBNbYgeQY7cU2GQ+PSkSROe3g7JOQqnT6OOuX0rXKY0ZNoVnlB2Y/H5FWnnJ6QrPlK3fNg9+bQ=="],
"@expo/image-utils": ["@expo/image-utils@0.11.6", "", { "dependencies": { "@expo/require-utils": "^57.0.6", "@expo/spawn-async": "^1.8.0", "chalk": "^4.0.0", "getenv": "^2.0.0", "jimp-compact": "0.16.1", "parse-png": "^2.1.0", "semver": "^7.6.0" } }, "sha512-rmMpn7QGp6pUGRCQ3vvB9RP/vqq2q/PpToRh4dL/FbhSZOLZ1YTotpuLHf71FGOEfgKil/4PCd3hkWe/CCeiEg=="],
- "@expo/inline-modules": ["@expo/inline-modules@0.1.7", "", { "dependencies": { "@expo/config-plugins": "~57.0.9" } }, "sha512-Bz/khd1gIJqDkje7t5ejD5e9jFbm4xEJzWSwRKadRo6gruepbw1xJ3Eb+e58OS8fY1ZNQYjzZbspnuph67sN0g=="],
+ "@expo/inline-modules": ["@expo/inline-modules@0.1.8", "", { "dependencies": { "@expo/config-plugins": "~57.0.10" } }, "sha512-5OCHzv/VHVuam9UT6/yeArtUnmkp5VdAH6k4JwdRzK/34ukzdjQK7B9n71fiswZKtm2MmZlPuy4hJo2Ao8CZNw=="],
"@expo/json-file": ["@expo/json-file@11.0.1", "", { "dependencies": { "@babel/code-frame": "^7.20.0", "json5": "^2.2.3" } }, "sha512-zxHWj4MKKMAL29ZQSY/Fssx4Thluk40JmuGNaeS078wy/NhlFhnVi+rHHunulE3xJAJ0CM73m8VK2+GkF9eRwQ=="],
- "@expo/local-build-cache-provider": ["@expo/local-build-cache-provider@57.0.8", "", { "dependencies": { "@expo/config": "~57.0.9", "chalk": "^4.1.2" } }, "sha512-SEdE0pAQrr90bRh3MNR0ZuwoIBw390cYdFgbn7Vk0Mtm9EHaBfP7kYj+2hXnXZJgOEm3/JMtfoD3rV7rWA9FGg=="],
+ "@expo/local-build-cache-provider": ["@expo/local-build-cache-provider@57.0.9", "", { "dependencies": { "@expo/config": "~57.0.10", "chalk": "^4.1.2" } }, "sha512-hox13/K/dqvKG7StLYs9nwyNR/mJ5ieAkNN8jTee6liMPyUQTfpzgticRyWnVxj49Fr8PpTaQab/ArYVEBJOcQ=="],
"@expo/log-box": ["@expo/log-box@57.0.4", "", { "dependencies": { "@expo/dom-webview": "^57.0.1", "anser": "^1.4.9", "stacktrace-parser": "^0.1.10" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-IxwS9s1L2muj8mj8AQSuiy7u8OFJdc02NRFo2me/Tj6DiaeG5SREqmpBE4rQpR2cadqSg5jl8Qab8Cjie616dg=="],
@@ -371,7 +377,7 @@
"@expo/plist": ["@expo/plist@0.8.1", "", { "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" } }, "sha512-3gTReGIUm0oRaMClsAJYxBnVPCl6fVpsl8HS+DTVxDhW4GyVyxg9E/Znm3BvcHtUJ51RJJI14pC1wvrNilCRHw=="],
- "@expo/prebuild-config": ["@expo/prebuild-config@57.0.17", "", { "dependencies": { "@expo/config": "~57.0.10", "@expo/config-plugins": "~57.0.10", "@expo/config-types": "^57.0.2", "@expo/image-utils": "^0.11.6", "@expo/json-file": "^11.0.1", "@react-native/normalize-colors": "0.86.3", "debug": "^4.3.1", "expo-modules-autolinking": "~57.0.14", "resolve-from": "^5.0.0", "semver": "^7.6.0" } }, "sha512-PnPw4QT8afAA1RqTktK89+JpyJz46cZsZx6gEAe6QCHgOHc7dlA0rXlOFy0xbHpBnYT8x7wl6vTz2ydT99gykw=="],
+ "@expo/prebuild-config": ["@expo/prebuild-config@57.0.18", "", { "dependencies": { "@expo/config": "~57.0.10", "@expo/config-plugins": "~57.0.10", "@expo/config-types": "^57.0.2", "@expo/image-utils": "^0.11.6", "@expo/json-file": "^11.0.1", "@react-native/normalize-colors": "0.86.3", "debug": "^4.3.1", "expo-modules-autolinking": "~57.0.15", "resolve-from": "^5.0.0", "semver": "^7.6.0" } }, "sha512-q/Jijt1H2GT1TlD+C9UOLmErjujjieUs+Rpdo6+L5IBRaW+gXJY0oFqlpyKvOwziQIv4AYL7vZFn6I+OyRgRXQ=="],
"@expo/require-utils": ["@expo/require-utils@57.0.6", "", { "dependencies": { "@babel/code-frame": "^7.20.0", "@babel/core": "^7.25.2", "@babel/plugin-transform-modules-commonjs": "^7.24.8" }, "peerDependencies": { "typescript": "^5.0.0 || ^5.0.0-0 || ^6.0.0 || ^7.0.0" }, "optionalPeers": ["typescript"] }, "sha512-PKWfFdMgwWrlSP6NGplCi2nK0dEM5MeJ2Cj1ijr5VWTmZ+pKsJsJw2dWkBtlFDyxMY8eUjCPZ7hoqFEnbc0Pcw=="],
@@ -385,7 +391,7 @@
"@expo/sudo-prompt": ["@expo/sudo-prompt@9.3.2", "", {}, "sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw=="],
- "@expo/ui": ["@expo/ui@57.0.22", "", { "dependencies": { "sf-symbols-typescript": "^2.1.0", "vaul": "^1.1.2" }, "peerDependencies": { "@babel/core": "*", "expo": "*", "react": "*", "react-dom": "*", "react-native": "*", "react-native-worklets": "*" }, "optionalPeers": ["@babel/core", "react-dom", "react-native-worklets"] }, "sha512-v0p397fI7XhYBt+O5Q4MCWKB1lwR8cA2xjoQbc/V6W+EmP9qU1sHh0eQ7QHdeE/lyNjWW4wIut0/H/8OjcHo3w=="],
+ "@expo/ui": ["@expo/ui@57.0.23", "", { "dependencies": { "sf-symbols-typescript": "^2.1.0", "vaul": "^1.1.2" }, "peerDependencies": { "@babel/core": "*", "expo": "*", "react": "*", "react-dom": "*", "react-native": "*", "react-native-worklets": "*" }, "optionalPeers": ["@babel/core", "react-dom", "react-native-worklets"] }, "sha512-5QW1uzs8cGBlBKQvzNLDmmAeTCiJn4HVCYC2tddyOtXnXsUKGY7Rvgw6otr9Q2rPcOY5mC34wJIspViamvptww=="],
"@expo/ws-tunnel": ["@expo/ws-tunnel@2.0.1", "", { "peerDependencies": { "ws": "^8.0.0" } }, "sha512-mljsRffbOHzmSimYQbQu9h9NR3alwCCwEEouRGFL4QRTt8dNvsNtKEPa/5jHmtDLDtsh4xXsWKwzjN9BTpeXHQ=="],
@@ -425,6 +431,14 @@
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.2.5", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" } }, "sha512-HshSvXzmBxNzqejd3k/KemgQ3hdREUYFRRrgxOZ+gPljDIsO2SDZrVZMd4Z/HsxudQbE62OVyVFE8z9BCurfjA=="],
+ "@noble/ciphers": ["@noble/ciphers@2.4.0", "", {}, "sha512-AnjFn0Jv92laAkvMrghlFZq4qQCIN/4DxFV/eooqtC2YTjB7kBeLMS2T9KJX4Dn+ZVXLOwK0lSgqDtx9gvxtiw=="],
+
+ "@noble/curves": ["@noble/curves@2.4.0", "", { "dependencies": { "@noble/hashes": "2.4.0" } }, "sha512-P4/62zrgfH33CneE3Dn4WhJVA22YUU0eR51wKIan4NVRvwsA0YnPTwWGpNbpuacSujmSFLvyzpyuR30+fbq2Ew=="],
+
+ "@noble/hashes": ["@noble/hashes@2.4.0", "", {}, "sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA=="],
+
+ "@noble/post-quantum": ["@noble/post-quantum@0.7.1", "", { "dependencies": { "@noble/ciphers": "2.4.0", "@noble/curves": "2.4.0", "@noble/hashes": "2.4.0" } }, "sha512-+P9981IiAnVh+rmcubozzVwrEy3XsN/tMhTnvsjV9VDaYpOnNCqWqKo2FLWxbu92YHfjGIlE5XnW175UK+ln+Q=="],
+
"@noble/secp256k1": ["@noble/secp256k1@3.2.0", "", {}, "sha512-Z3ZAWOTxJ0EuTuZTi7Y69iK7GgrLHh8sgm45lVDhg/b3Nk1TpAiKhick2KkZisHuupeepSkyIydN/J459SdX1w=="],
"@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="],
@@ -437,6 +451,8 @@
"@optique/run": ["@optique/run@1.3.4", "", { "dependencies": { "@optique/core": "1.3.4" } }, "sha512-oyW1b6DIa4Fo/nGKT7Ph/7zsuAE96O08d9dU7BqdJb45m1vrzg4dgMeqLikKhA5jOJ2aIsJhsqKClCmepYHWug=="],
+ "@pkgr/core": ["@pkgr/core@0.3.6", "", {}, "sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA=="],
+
"@radix-ui/primitive": ["@radix-ui/primitive@1.1.7", "", {}, "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q=="],
"@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.16", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-primitive": "2.1.11", "@radix-ui/react-slot": "1.4.0" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-yEQrjfI5eBbeYEJXOASpm+gs9eNHY8+UjkZ81GzcuwdLrSMOlEM8jnGoGdeGo92oiBMkuwZUhHTsnsrDiJLcyA=="],
@@ -479,8 +495,14 @@
"@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw=="],
+ "@react-native-async-storage/async-storage": ["@react-native-async-storage/async-storage@2.2.0", "", { "dependencies": { "merge-options": "^3.0.4" }, "peerDependencies": { "react-native": "^0.0.0-0 || >=0.65 <1.0" } }, "sha512-gvRvjR5JAaUZF8tv2Kcq/Gbt3JHwbKFYfmb445rhOj6NUMx3qPLixmDx5pZAyb9at1bYvJ4/eTUipU5aki45xw=="],
+
"@react-native-masked-view/masked-view": ["@react-native-masked-view/masked-view@0.3.2", "", { "peerDependencies": { "react": ">=16", "react-native": ">=0.57" } }, "sha512-XwuQoW7/GEgWRMovOQtX3A4PrXhyaZm0lVUiY8qJDvdngjLms9Cpdck6SmGAUNqQwcj2EadHC1HwL0bEyoa/SQ=="],
+ "@react-native-vector-icons/common": ["@react-native-vector-icons/common@13.0.4", "", { "dependencies": { "find-up": "^8.0.0", "picocolors": "^1.1.1", "plist": "^3.1.0" }, "peerDependencies": { "@react-native-vector-icons/get-image": "^13.0.2", "@react-native/assets-registry": "*", "expo-font": "*", "react": "*", "react-native": "*" }, "optionalPeers": ["@react-native-vector-icons/get-image", "@react-native/assets-registry", "expo-font"], "bin": { "rnvi-update-plist": "lib/commonjs/scripts/updatePlist.js" } }, "sha512-1MvNyRf3KIW747rJsmka/+EdEwV92ccVHedEXFV2kQ0lo6D7uvrIBTtffeDidN4//l8+R4bN3IiQv0tXQzaRXQ=="],
+
+ "@react-native-vector-icons/fontawesome6": ["@react-native-vector-icons/fontawesome6@13.1.5", "", { "dependencies": { "@react-native-vector-icons/common": "^13.0.4" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" }, "optionalPeers": ["expo"] }, "sha512-72z/rpMBIE7NZunmlGeKyntfbPxDkPGtQM9V4XVLptwysmx9Jbnc5STN2zKVtPCRWSifykIuiNNAc/yAomIrBg=="],
+
"@react-native/assets-registry": ["@react-native/assets-registry@0.86.3", "", {}, "sha512-TDhgCZA4wjJg84d5A9swiOQYPIWSKEEVdg9IwMFZDupQzW/F3QoLUrfAJOcalgqTDA9/buTB8awhE3Whwg6u9Q=="],
"@react-native/babel-plugin-codegen": ["@react-native/babel-plugin-codegen@0.86.3", "", { "dependencies": { "@babel/traverse": "^7.29.0", "@react-native/codegen": "0.86.3" } }, "sha512-O6Xza4JBGPIU8J7YbKTyBoYL4thpy8jMW/oaLDWdAyOwYHKIjK47pAL5HUEbOe2bWz2PEKjbYRF2ApkJv1ottQ=="],
@@ -551,6 +573,8 @@
"@types/debug": ["@types/debug@4.1.13", "", { "dependencies": { "@types/ms": "*" } }, "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw=="],
+ "@types/emscripten": ["@types/emscripten@1.41.6", "", {}, "sha512-uN+9i8bFT5CUcZfyIEYDrSueACEyKGbUs5kC/72DGlZZoinh84sJfVV0i8UOJD1asdzkvLPBRrKs41kZ8MdEXg=="],
+
"@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="],
"@types/hammerjs": ["@types/hammerjs@2.0.46", "", {}, "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw=="],
@@ -643,7 +667,7 @@
"@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.12.2", "", { "os": "win32", "cpu": "x64" }, "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA=="],
- "@xmldom/xmldom": ["@xmldom/xmldom@0.8.15", "", {}, "sha512-/5NV/vDALVFDXgLmfsy9TRCBlKwO2LNBFzpzvb9iIj+jR+eSc6DLYYvVOdivT/jm7MtU6TebYuRmzEOI7w40UA=="],
+ "@xmldom/xmldom": ["@xmldom/xmldom@0.9.12", "", {}, "sha512-5AXjrcMClTryPe9LgZrygpB1lj7s0S9E0+W+AHaVKAVyHanafK86iPSvG5xHVSp/jC+VH1UXu0TAEmY279xH7A=="],
"abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
@@ -717,12 +741,16 @@
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
+ "barcode-detector": ["barcode-detector@3.2.2", "", { "dependencies": { "zxing-wasm": "3.1.3" } }, "sha512-/4QOrrNrCRmDSBWiiP4aC72dnkuXUEdcFRidHgbPRXUpy82XcCMvJssI6fEs6JT42LS7DvBVZO70qasJbYKyrA=="],
+
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
"baseline-browser-mapping": ["baseline-browser-mapping@2.11.28", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-br0LOmjJqr6Rt4AEkadbcNf67Zpo7EPwKsWbjnRrJIa1QYFHQBqM4C7F2WTbV8ahTEcg1Eo+Y0aWjjORInYFyA=="],
"big-integer": ["big-integer@1.6.52", "", {}, "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg=="],
+ "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
+
"bplist-creator": ["bplist-creator@0.1.0", "", { "dependencies": { "stream-buffers": "2.2.x" } }, "sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg=="],
"bplist-parser": ["bplist-parser@0.3.2", "", { "dependencies": { "big-integer": "1.6.x" } }, "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ=="],
@@ -807,6 +835,12 @@
"css-in-js-utils": ["css-in-js-utils@3.1.0", "", { "dependencies": { "hyphenate-style-name": "^1.0.3" } }, "sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A=="],
+ "css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="],
+
+ "css-tree": ["css-tree@1.1.3", "", { "dependencies": { "mdn-data": "2.0.14", "source-map": "^0.6.1" } }, "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q=="],
+
+ "css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="],
+
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
"data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="],
@@ -817,6 +851,8 @@
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
+ "decamelize": ["decamelize@1.2.0", "", {}, "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="],
+
"decode-uri-component": ["decode-uri-component@0.2.2", "", {}, "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ=="],
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
@@ -837,10 +873,20 @@
"detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
+ "dijkstrajs": ["dijkstrajs@1.0.3", "", {}, "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="],
+
"dnssd-advertise": ["dnssd-advertise@1.1.6", "", {}, "sha512-Ndrrf6BMPalkQPd/zubL+4YghH2J9NspapQ09uDXwYbvOPkP0oaqf5CkcwJ0b50kS2O3ul6yVu+jz+RY62Cejg=="],
"doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
+ "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
+
+ "domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
+
+ "domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
+
+ "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
+
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
@@ -853,6 +899,8 @@
"enhanced-resolve": ["enhanced-resolve@5.26.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw=="],
+ "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
+
"error-stack-parser": ["error-stack-parser@2.1.4", "", { "dependencies": { "stackframe": "^1.3.4" } }, "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ=="],
"es-abstract": ["es-abstract@1.24.2", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg=="],
@@ -883,6 +931,8 @@
"eslint-config-expo": ["eslint-config-expo@57.0.2", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "^8.59.0", "@typescript-eslint/parser": "^8.59.0", "eslint-import-resolver-typescript": "^3.6.3", "eslint-plugin-expo": "^1.1.0", "eslint-plugin-import": "^2.30.0", "eslint-plugin-react": "^7.37.3", "eslint-plugin-react-hooks": "^7.0.0", "globals": "^16.0.0" }, "peerDependencies": { "eslint": ">=8.10" } }, "sha512-dOWkx+MWclLVnYpPPBzas4sfnPaAjk+fe/dbvJSzbWtHUR4fGGaTT/lTcOpxqhwRIlTKKBUYGKude0tcTBsZHg=="],
+ "eslint-config-prettier": ["eslint-config-prettier@10.1.8", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w=="],
+
"eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.10", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.16.1", "resolve": "^2.0.0-next.6" } }, "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ=="],
"eslint-import-resolver-typescript": ["eslint-import-resolver-typescript@3.10.1", "", { "dependencies": { "@nolyfill/is-core-module": "1.0.39", "debug": "^4.4.0", "get-tsconfig": "^4.10.0", "is-bun-module": "^2.0.0", "stable-hash": "^0.0.5", "tinyglobby": "^0.2.13", "unrs-resolver": "^1.6.2" }, "peerDependencies": { "eslint": "*", "eslint-plugin-import": "*", "eslint-plugin-import-x": "*" }, "optionalPeers": ["eslint-plugin-import", "eslint-plugin-import-x"] }, "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ=="],
@@ -893,6 +943,8 @@
"eslint-plugin-import": ["eslint-plugin-import@2.32.0", "", { "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", "array.prototype.findlastindex": "^1.2.6", "array.prototype.flat": "^1.3.3", "array.prototype.flatmap": "^1.3.3", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", "eslint-module-utils": "^2.12.1", "hasown": "^2.0.2", "is-core-module": "^2.16.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "object.groupby": "^1.0.3", "object.values": "^1.2.1", "semver": "^6.3.1", "string.prototype.trimend": "^1.0.9", "tsconfig-paths": "^3.15.0" }, "peerDependencies": { "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA=="],
+ "eslint-plugin-prettier": ["eslint-plugin-prettier@5.5.6", "", { "dependencies": { "prettier-linter-helpers": "^1.0.1", "synckit": "^0.11.13" }, "peerDependencies": { "@types/eslint": ">=8.0.0", "eslint": ">=8.0.0", "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", "prettier": ">=3.0.0" }, "optionalPeers": ["@types/eslint", "eslint-config-prettier"] }, "sha512-ifetmTcxWfz+4qRW3pH/ujdTq2jQIj59AxJMIN26K5avYgU8dxycUETQonWiW+wPrYXA0j3Try0l1CnwVQtDqQ=="],
+
"eslint-plugin-react": ["eslint-plugin-react@7.37.5", "", { "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA=="],
"eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@7.1.1", "", { "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", "hermes-parser": "^0.25.1", "zod": "^3.25.0 || ^4.0.0", "zod-validation-error": "^3.5.0 || ^4.0.0" }, "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" } }, "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g=="],
@@ -919,17 +971,21 @@
"event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="],
- "expo": ["expo@57.0.27", "", { "dependencies": { "@babel/runtime": "^7.20.0", "@expo/cli": "^57.0.28", "@expo/config": "~57.0.10", "@expo/config-plugins": "~57.0.10", "@expo/devtools": "~57.0.1", "@expo/dom-webview": "~57.0.1", "@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.13", "@ungap/structured-clone": "^1.3.0", "babel-preset-expo": "~57.0.14", "expo-asset": "~57.0.19", "expo-constants": "~57.0.21", "expo-file-system": "~57.0.7", "expo-font": "~57.0.4", "expo-keep-awake": "~57.0.2", "expo-modules-autolinking": "~57.0.14", "expo-modules-core": "~57.0.21", "pretty-format": "^29.7.0", "react-refresh": "^0.14.2", "whatwg-url-minimum": "^0.1.2" }, "peerDependencies": { "@expo/metro-runtime": "*", "react": "*", "react-dom": "*", "react-native": "*", "react-native-web": "*", "react-native-webview": "*" }, "optionalPeers": ["@expo/metro-runtime", "react-dom", "react-native-web", "react-native-webview"], "bin": { "expo": "bin/cli", "fingerprint": "bin/fingerprint", "expo-modules-autolinking": "bin/autolinking" } }, "sha512-Jc3nByHsmzjakvIrqV3/3viVj+jFw1OCfMc+WpClemwL3/hSrS7ktkVCACSTI7/j+1uH+eHmBJwe1kujDLMnbQ=="],
+ "expo": ["expo@57.0.28", "", { "dependencies": { "@babel/runtime": "^7.20.0", "@expo/cli": "^57.0.29", "@expo/config": "~57.0.10", "@expo/config-plugins": "~57.0.10", "@expo/devtools": "~57.0.1", "@expo/dom-webview": "~57.0.1", "@expo/fingerprint": "^0.20.14", "@expo/local-build-cache-provider": "^57.0.9", "@expo/log-box": "^57.0.4", "@expo/metro": "~56.0.2", "@expo/metro-config": "~57.0.13", "@ungap/structured-clone": "^1.3.0", "babel-preset-expo": "~57.0.15", "expo-asset": "~57.0.19", "expo-constants": "~57.0.21", "expo-file-system": "~57.0.7", "expo-font": "~57.0.4", "expo-keep-awake": "~57.0.2", "expo-modules-autolinking": "~57.0.15", "expo-modules-core": "~57.0.22", "pretty-format": "^29.7.0", "react-refresh": "^0.14.2", "whatwg-url-minimum": "^0.1.2" }, "peerDependencies": { "@expo/metro-runtime": "*", "react": "*", "react-dom": "*", "react-native": "*", "react-native-web": "*", "react-native-webview": "*" }, "optionalPeers": ["@expo/metro-runtime", "react-dom", "react-native-web", "react-native-webview"], "bin": { "expo": "bin/cli", "fingerprint": "bin/fingerprint", "expo-modules-autolinking": "bin/autolinking" } }, "sha512-Bh9PgT1wLdcSOi8uXvUtHYh12zz9kx3zjD8Pm7O8EAsZW0W42dVog3/UhY2qJnlOkSDpTQJTnX8jsi2J5xn2OA=="],
"expo-asset": ["expo-asset@57.0.19", "", { "dependencies": { "@expo/image-utils": "^0.11.6", "expo-constants": "~57.0.21" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-y9ymKidyPBDUFXljbvo7LNgivQvMhEJEoEXi6aLRugUxRDUvAKoZsy+KB5UXSjLMS7l7JNDwYKk5thovI0MLog=="],
+ "expo-blur": ["expo-blur@57.0.3", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-uyBeJwxIuXzWXXNrnRub5pcF8VzQsiOz0VBTWDpMG2UztmaFyfAz6pI/GCwRtWpHhiOpLoJj79WLdg1GMB+9cg=="],
+
+ "expo-camera": ["expo-camera@57.0.6", "", { "dependencies": { "barcode-detector": "^3.0.0" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*", "react-native-web": "*" }, "optionalPeers": ["react-native-web"] }, "sha512-eswB77bOzQ2OEvll/hV5VnzywaICFnYF21HIwcyokebFcSTSMfGQk8+VW06DRec9JnWuRExs0QXlfxyMPjS6KQ=="],
+
"expo-constants": ["expo-constants@57.0.21", "", { "dependencies": { "@expo/env": "~2.4.3" }, "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-D5fV2GELYGbNkoiEVZZp5zrUXZDaAx6ZzErDzGirm3jCjOhaE8iU/GyJiB0ZQE+K4Bg8gY82nIQ/RZR8CJLVjA=="],
- "expo-dev-client": ["expo-dev-client@57.0.19", "", { "dependencies": { "expo-dev-launcher": "~57.0.20", "expo-dev-menu": "~57.0.18", "expo-dev-menu-interface": "~57.0.0", "expo-manifests": "~57.0.2", "expo-updates-interface": "~57.0.2" }, "peerDependencies": { "expo": "*" } }, "sha512-7giMK6BLL685GECHem2UAQvS473ImZLi5l+Q5tq+XSyv6I9UNVPs0p07mKwgBzq8z7DR3d4nTl702ziFxTjO3Q=="],
+ "expo-dev-client": ["expo-dev-client@57.0.20", "", { "dependencies": { "expo-dev-launcher": "~57.0.21", "expo-dev-menu": "~57.0.20", "expo-dev-menu-interface": "~57.0.0", "expo-manifests": "~57.0.2", "expo-updates-interface": "~57.0.2" }, "peerDependencies": { "expo": "*" } }, "sha512-ko5FzWsUPuG4aiQfmqxQA5yAdmal6SSICB70KJLChQYzWo+XDoTpXRgEpzQSr1Ku8KyKW0lkEa48xbnKSgd1xQ=="],
- "expo-dev-launcher": ["expo-dev-launcher@57.0.20", "", { "dependencies": { "@expo/schema-utils": "^57.0.2", "expo-dev-menu": "~57.0.18", "expo-manifests": "~57.0.2" }, "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-F6oAHaDn9j3L/gyMGE+OalriX6PHZWnZLxpN5jc6crTrp8f6hSwGhmsHpAvdOzijfMjyi3R2UkcISFsCIIQYFA=="],
+ "expo-dev-launcher": ["expo-dev-launcher@57.0.21", "", { "dependencies": { "@expo/schema-utils": "^57.0.2", "expo-dev-menu": "~57.0.20", "expo-manifests": "~57.0.2" }, "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-dXa1bhCEtSXn4y34rydBvArmoZtcLK7zY0FN3x1JFPTGh81OXUGZWhyzLCAI9nhI23L/MTy7cGCeNyeJmC1ITw=="],
- "expo-dev-menu": ["expo-dev-menu@57.0.19", "", { "dependencies": { "expo-dev-menu-interface": "~57.0.0" }, "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-7mE4TytDPgF+a5hisGQKpzd8WfEb7cQOHNARAXSJqogR33qeHVaYfXtsC/DLMV/oAhPr5/rItknT1LyIWbQZUQ=="],
+ "expo-dev-menu": ["expo-dev-menu@57.0.20", "", { "dependencies": { "expo-dev-menu-interface": "~57.0.0" }, "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-xpf7Rkj++sBA285GqWVJTtl5j/TychD980GKqWA5lVFKM41+xUNmyPMOfO18heAKvX9i620ZHVoHiBGD+6pCdA=="],
"expo-dev-menu-interface": ["expo-dev-menu-interface@57.0.0", "", { "peerDependencies": { "expo": "*" } }, "sha512-F47VdzOHYc19FhI/jBgctpO8a5UskTIxG6a1E5t3W5gF8VImuvBQffdXXfLHhsuCl7dS3v3U0R45cleeVXO1Zg=="],
@@ -951,19 +1007,19 @@
"expo-manifests": ["expo-manifests@57.0.2", "", { "dependencies": { "expo-json-utils": "~57.0.2" }, "peerDependencies": { "expo": "*" } }, "sha512-pPIIcKR73OShwlWMg8H90WRzKUd2krDdKlKAwuQV1FtUwE7H3hQEFw0ePFQY9yFoGMF/77ryYwNKfI5umMcCog=="],
- "expo-modules-autolinking": ["expo-modules-autolinking@57.0.14", "", { "dependencies": { "@expo/require-utils": "^57.0.6", "@expo/spawn-async": "^1.8.0", "chalk": "^4.1.0", "commander": "^7.2.0" }, "bin": { "expo-modules-autolinking": "bin/expo-modules-autolinking.js" } }, "sha512-dWql3NU2tvgfwkdZY3u9Sy3iWRA4IGDk91aOGZAJm3ZqudFxDRzAxlh8oQCVQ81Z+U9VmlUXw/4P9CbL33Ty0A=="],
+ "expo-modules-autolinking": ["expo-modules-autolinking@57.0.15", "", { "dependencies": { "@expo/require-utils": "^57.0.6", "@expo/spawn-async": "^1.8.0", "chalk": "^4.1.0", "commander": "^7.2.0" }, "bin": { "expo-modules-autolinking": "bin/expo-modules-autolinking.js" } }, "sha512-TY7Sy7MaIBFNMTTsmNtrkdyFwG3abE4gU2WJ6B4Qh9zXOMhtghHvVYTPh9bujHOY/2bscj8dOTqXnZ8iTVtwmg=="],
- "expo-modules-core": ["expo-modules-core@57.0.21", "", { "dependencies": { "@expo/expo-modules-macros-plugin": "0.6.1", "expo-modules-jsi": "~57.1.1", "invariant": "^2.2.4" }, "peerDependencies": { "react": "*", "react-native": "*", "react-native-worklets": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0" }, "optionalPeers": ["react-native-worklets"] }, "sha512-g0pf9/tQ6IdJWCzr6PDrEiYT8mTaOhMRnXm3T2XtSzJJaNYgnyyDonkkPN1wd2+PqojIBPhytqqzeLxjM5y/Wg=="],
+ "expo-modules-core": ["expo-modules-core@57.0.22", "", { "dependencies": { "@expo/expo-modules-macros-plugin": "0.6.1", "expo-modules-jsi": "~57.1.2", "invariant": "^2.2.4" }, "peerDependencies": { "react": "*", "react-native": "*", "react-native-worklets": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0" }, "optionalPeers": ["react-native-worklets"] }, "sha512-IQTSHU690fleLZ6b36OCqt04KFStxA/k7El2vweOWhlFZ58hpNSQMK2Fosc1nIvlOnqqVRDcTAq+prJEOHLmBg=="],
- "expo-modules-jsi": ["expo-modules-jsi@57.1.1", "", { "peerDependencies": { "react-native": "*" } }, "sha512-ssnwz3ZuIgCbZj5A3J6NlhQrXZojg+BjkeWrOWqVJhyMHlV/A/cRT7s986chKku97BFt6ucmDu6/BCzrV45Wcw=="],
+ "expo-modules-jsi": ["expo-modules-jsi@57.1.2", "", { "peerDependencies": { "react-native": "*" } }, "sha512-DYhBwO0fqcKIUma4jF16zRDP/m6ZbawEZbo4eX3gdekREIFqrrz8HKWXydgNUqOyOQcUkdrkZatxi2I9+V6E8g=="],
- "expo-router": ["expo-router@57.0.25", "", { "dependencies": { "@expo/log-box": "^57.0.4", "@expo/metro-runtime": "^57.0.16", "@expo/schema-utils": "^57.0.2", "@expo/ui": "^57.0.22", "@radix-ui/react-slot": "^1.2.0", "@radix-ui/react-tabs": "^1.1.12", "@react-native-masked-view/masked-view": "^0.3.2", "client-only": "^0.0.1", "color": "^4.2.3", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", "expo-glass-effect": "^57.0.4", "expo-server": "^57.0.3", "expo-symbols": "^57.0.3", "fast-deep-equal": "^3.1.3", "invariant": "^2.2.4", "nanoid": "^3.3.18", "query-string": "^7.1.3", "react-fast-compare": "^3.2.2", "react-is": "^19.1.0", "react-native-drawer-layout": "^4.2.2", "react-native-screens": "^4.26.0", "server-only": "^0.0.1", "sf-symbols-typescript": "^2.1.0", "shallowequal": "^1.1.0", "standard-navigation": "^0.0.5", "vaul": "^1.1.2" }, "peerDependencies": { "@testing-library/react-native": ">= 13.2.0", "expo": "*", "expo-constants": "^57.0.21", "expo-linking": "^57.0.12", "react": "*", "react-dom": "*", "react-native": "*", "react-native-gesture-handler": "*", "react-native-reanimated": "*", "react-native-safe-area-context": ">= 5.4.0", "react-native-web": "*", "react-server-dom-webpack": "~19.0.4 || ~19.1.5 || ~19.2.4" }, "optionalPeers": ["@testing-library/react-native", "react-dom", "react-native-gesture-handler", "react-native-reanimated", "react-native-web", "react-server-dom-webpack"] }, "sha512-S/xWUGeVqMozG8gFJT0CHOVErZEbQnsAEAqlBIvHJSUZPH0hBcxy2kduUrV7ZDLNI4xCPIlOJ4SI29X/1z/hxw=="],
+ "expo-router": ["expo-router@57.0.26", "", { "dependencies": { "@expo/log-box": "^57.0.4", "@expo/metro-runtime": "^57.0.16", "@expo/schema-utils": "^57.0.2", "@expo/ui": "^57.0.23", "@radix-ui/react-slot": "^1.2.0", "@radix-ui/react-tabs": "^1.1.12", "@react-native-masked-view/masked-view": "^0.3.2", "client-only": "^0.0.1", "color": "^4.2.3", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", "expo-glass-effect": "^57.0.4", "expo-server": "^57.0.3", "expo-symbols": "^57.0.3", "fast-deep-equal": "^3.1.3", "invariant": "^2.2.4", "nanoid": "^3.3.18", "query-string": "^7.1.3", "react-fast-compare": "^3.2.2", "react-is": "^19.1.0", "react-native-drawer-layout": "^4.2.2", "react-native-screens": "^4.26.0", "server-only": "^0.0.1", "sf-symbols-typescript": "^2.1.0", "shallowequal": "^1.1.0", "standard-navigation": "^0.0.5", "vaul": "^1.1.2" }, "peerDependencies": { "@testing-library/react-native": ">= 13.2.0", "expo": "*", "expo-constants": "^57.0.21", "expo-linking": "^57.0.12", "react": "*", "react-dom": "*", "react-native": "*", "react-native-gesture-handler": "*", "react-native-reanimated": "*", "react-native-safe-area-context": ">= 5.4.0", "react-native-web": "*", "react-server-dom-webpack": "~19.0.4 || ~19.1.5 || ~19.2.4" }, "optionalPeers": ["@testing-library/react-native", "react-dom", "react-native-gesture-handler", "react-native-reanimated", "react-native-web", "react-server-dom-webpack"] }, "sha512-iSF+LEbbvGkYGA2j8qWvF1woxrA7EWHZF+SNdTPR7dmi6myPOJsDGdwfWlXA74Gns8T7AuyfuDsxIsqEJ45BhQ=="],
"expo-secure-store": ["expo-secure-store@57.0.4", "", { "peerDependencies": { "expo": "*" } }, "sha512-7zBA1RThURTXdP4w65kBzZNbcSsfArqVd+p+ETAnPRWXO8J296Qd0YaCVyRpJ0HSwlCnGdvOnqO+NgSreHsOhA=="],
"expo-server": ["expo-server@57.0.3", "", {}, "sha512-aK+LdKzauHSGmsOStZtyxdzv0zWssCkxTw3m4QuOhfDSJsZaMRTd9O41d8ixU/QfELTbaJ0oRNcF7JFV/7O9YQ=="],
- "expo-splash-screen": ["expo-splash-screen@57.0.9", "", { "dependencies": { "@expo/config-plugins": "~57.0.9", "@expo/image-utils": "^0.11.5", "xml2js": "0.6.0" }, "peerDependencies": { "expo": "*" } }, "sha512-7cKDn7uBwg0RkcmY015/5j9st4msWJ4zQiPmQdVZoppW3cFSbv05n7xhM6CjUU466Tx4VRTqDF21HuIHkmnEtA=="],
+ "expo-splash-screen": ["expo-splash-screen@57.0.10", "", { "dependencies": { "@expo/config-plugins": "~57.0.10", "@expo/image-utils": "^0.11.6", "xml2js": "0.6.0" }, "peerDependencies": { "expo": "*" } }, "sha512-W7++tMiyVMviWPTvaTXKpcOwwu5SbriwA2n0ewEhC+ic1LFaUNTUXiIXr95MVMSHaLRgtIG9dGyCTCb+NkekkA=="],
"expo-status-bar": ["expo-status-bar@57.0.1", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-Xwaq1gAoVRWx5dPG5VhT5RSbnI9OilhZnO5qoPBnUaBAa5VzRzfdS8q0/bsPt0jR2DKLtGuP0bQ6efMJ4RIMDg=="],
@@ -977,8 +1033,12 @@
"exponential-backoff": ["exponential-backoff@3.1.3", "", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="],
+ "fast-base64-decode": ["fast-base64-decode@1.0.0", "", {}, "sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q=="],
+
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
+ "fast-diff": ["fast-diff@1.3.0", "", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="],
+
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
"fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
@@ -1143,6 +1203,8 @@
"is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="],
+ "is-plain-obj": ["is-plain-obj@2.1.0", "", {}, "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA=="],
+
"is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="],
"is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="],
@@ -1259,8 +1321,12 @@
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
+ "mdn-data": ["mdn-data@2.0.14", "", {}, "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow=="],
+
"memoize-one": ["memoize-one@5.2.1", "", {}, "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="],
+ "merge-options": ["merge-options@3.0.4", "", { "dependencies": { "is-plain-obj": "^2.1.0" } }, "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ=="],
+
"merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="],
"metro": ["metro@0.84.5", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/core": "^7.25.2", "@babel/generator": "^7.29.1", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "accepts": "^2.0.0", "ci-info": "^2.0.0", "connect": "^3.6.5", "debug": "^4.4.0", "error-stack-parser": "^2.0.6", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "hermes-parser": "0.35.0", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "jsc-safe-url": "^0.2.2", "lodash.throttle": "^4.1.1", "metro-babel-transformer": "0.84.5", "metro-cache": "0.84.5", "metro-cache-key": "0.84.5", "metro-config": "0.84.5", "metro-core": "0.84.5", "metro-file-map": "0.84.5", "metro-resolver": "0.84.5", "metro-runtime": "0.84.5", "metro-source-map": "0.84.5", "metro-symbolicate": "0.84.5", "metro-transform-plugins": "0.84.5", "metro-transform-worker": "0.84.5", "mime-types": "^3.0.1", "nullthrows": "^1.1.1", "serialize-error": "^2.1.0", "source-map": "^0.5.6", "throat": "^5.0.0", "ws": "^7.5.10", "yargs": "^17.6.2" }, "bin": { "metro": "src/cli.js" } }, "sha512-r1liLkyFZMVSEMNjU1CJU5pRzs3NdkxHqXS60O25c0rCIqAR+cGk7rPydw/g0WAIKVXojIBIF45yYBPagJGcgw=="],
@@ -1339,6 +1405,8 @@
"npm-package-arg": ["npm-package-arg@11.0.3", "", { "dependencies": { "hosted-git-info": "^7.0.0", "proc-log": "^4.0.0", "semver": "^7.3.5", "validate-npm-package-name": "^5.0.0" } }, "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw=="],
+ "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
+
"nullthrows": ["nullthrows@1.1.1", "", {}, "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw=="],
"ob1": ["ob1@0.84.6", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-+s+6zjd0X68hfAcn92NsEPXnSYOF3O7exw/Fj4UfRTB+UL+Tdjdkzu5/4WquXlzqgvNm8FWuuW6/0Yd8S2rYxw=="],
@@ -1377,6 +1445,8 @@
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
+ "p-try": ["p-try@2.2.0", "", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="],
+
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
"parse-png": ["parse-png@2.1.0", "", { "dependencies": { "pngjs": "^3.3.0" } }, "sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ=="],
@@ -1397,7 +1467,7 @@
"plist": ["plist@3.1.1", "", { "dependencies": { "@xmldom/xmldom": "^0.9.10", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" } }, "sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA=="],
- "pngjs": ["pngjs@3.4.0", "", {}, "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w=="],
+ "pngjs": ["pngjs@5.0.0", "", {}, "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw=="],
"possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
@@ -1409,6 +1479,8 @@
"prettier": ["prettier@3.9.10", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA=="],
+ "prettier-linter-helpers": ["prettier-linter-helpers@1.0.1", "", { "dependencies": { "fast-diff": "^1.1.2" } }, "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg=="],
+
"prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.5.14", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig-melody": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-import-sort": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-style-order": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig-melody", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-import-sort", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-style-order", "prettier-plugin-svelte"] }, "sha512-Puaz+wPUAhFp8Lo9HuciYKM2Y2XExESjeT+9NQoVFXZsPPnc9VYss2SpxdQ6vbatmt8/4+SN0oe0I1cPDABg9Q=="],
"pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="],
@@ -1425,6 +1497,8 @@
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
+ "qrcode": ["qrcode@1.5.4", "", { "dependencies": { "dijkstrajs": "^1.0.1", "pngjs": "^5.0.0", "yargs": "^15.3.1" }, "bin": { "qrcode": "bin/qrcode" } }, "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg=="],
+
"query-string": ["query-string@7.1.3", "", { "dependencies": { "decode-uri-component": "^0.2.2", "filter-obj": "^1.1.0", "split-on-first": "^1.0.0", "strict-uri-encode": "^2.0.0" } }, "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg=="],
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
@@ -1445,22 +1519,32 @@
"react-native-css": ["react-native-css@3.1.0-rc.0", "", { "dependencies": { "@types/debug": "^4.1.12", "babel-plugin-react-compiler": "^19.1.0-rc.2", "colorjs.io": "0.7.0", "comment-json": "^4.2.5", "debug": "^4.4.1" }, "peerDependencies": { "@expo/metro-config": ">=54", "lightningcss": ">=1.27.0", "react": ">=19", "react-native": ">=0.81" } }, "sha512-/wfPJ8pXiPxvHUs0QcocN3ViV4QJdnXpKukNirZJcJ/MeZKrStSEHm2rBq+KuZm8/2XDl4GfeuaVvJctYGtdgA=="],
+ "react-native-device-info": ["react-native-device-info@15.0.2", "", { "peerDependencies": { "react-native": "*" } }, "sha512-dd71eXG2l3Cwp66IvKNadMTB8fhU3PEjyVddI97sYan+D4bgIAUmgGDhbSOFvHcGavksb2U17kiQYaDiK2WK2g=="],
+
"react-native-drawer-layout": ["react-native-drawer-layout@4.2.11", "", { "dependencies": { "color": "^4.2.3", "use-latest-callback": "^0.2.4" }, "peerDependencies": { "react": ">= 18.2.0", "react-native": "*", "react-native-gesture-handler": ">= 2.0.0", "react-native-reanimated": ">= 2.0.0" } }, "sha512-Aua+LAZJ6L3v7ESHsNtxN2sO+hFQKsSz3EDKOLIpWni6PdQEJGFT0Mc2UnEsvFdfV743aHfODTAaHxsJ2D0uFw=="],
"react-native-gesture-handler": ["react-native-gesture-handler@2.32.0", "", { "dependencies": { "@egjs/hammerjs": "^2.0.17", "@types/react-test-renderer": "^19.1.0", "hoist-non-react-statics": "^3.3.0", "invariant": "^2.2.4" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-uYIMOKlKENORq2SABE+jIjbPU+h5I/sQKcq2v16zRq848nwEp1fWRVwML4QWqijc8UcXJC25o54S8GQd4Mf2OA=="],
+ "react-native-get-random-values": ["react-native-get-random-values@1.11.0", "", { "dependencies": { "fast-base64-decode": "^1.0.0" }, "peerDependencies": { "react-native": ">=0.56" } }, "sha512-4BTbDbRmS7iPdhYLRcz3PGFIpFJBwNZg9g42iwa2P6FOv9vZj/xJc678RZXnLNZzd0qd7Q3CCF6Yd+CU2eoXKQ=="],
+
"react-native-is-edge-to-edge": ["react-native-is-edge-to-edge@1.3.1", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-NIXU/iT5+ORyCc7p0z2nnlkouYKX425vuU1OEm6bMMtWWR9yvb+Xg5AZmImTKoF9abxCPqrKC3rOZsKzUYgYZA=="],
"react-native-mmkv": ["react-native-mmkv@4.3.2", "", { "peerDependencies": { "react": "*", "react-native": "*", "react-native-nitro-modules": "*" } }, "sha512-49OAyfkg0/TMWiWELZN6VuVQPZPhizwL4DTmp8b7B1md3dB/s3LH3mGfC3T+lp9W0y/rqxZMEnotLFTIbOAenQ=="],
"react-native-nitro-modules": ["react-native-nitro-modules@0.37.1", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-KpW6EQVQ/bfegpCGxN9Be+ndvsfj56t4IESEvCASu9gWpJa/NDfHpIeIwCOvZQ3eXmLEj6YD4wCLqcC1FJPr2w=="],
+ "react-native-nitro-totp": ["react-native-nitro-totp@2.0.1", "", { "peerDependencies": { "react": "*", "react-native": "*", "react-native-nitro-modules": "^0.29.6" } }, "sha512-E/B/qsIMzwifiypR+bJZ8v6VMMH5n/1kHWTyPr+x/ne7XyPHPx/7KEH7RMPBqw/kWDf/KfwEbb66xVstOE9IkA=="],
+
+ "react-native-qrcode-svg": ["react-native-qrcode-svg@6.3.26", "", { "dependencies": { "prop-types": "^15.8.0", "qrcode": "^1.5.4", "text-encoding": "^0.7.0" }, "peerDependencies": { "react": "*", "react-native": ">=0.63.4", "react-native-svg": ">=14.0.0" } }, "sha512-xhQywklf0qh5Z9ooy2rEuCrv6tRksVAeFUdYgv5y3fUkwQN53yp2Iu49QTgxj8QyjFgo0fYwee+knBBDzbNH+g=="],
+
"react-native-reanimated": ["react-native-reanimated@4.5.1", "", { "dependencies": { "react-native-is-edge-to-edge": "^1.3.1", "semver": "^7.7.3" }, "peerDependencies": { "react": "*", "react-native": "0.83 - 0.86", "react-native-worklets": "0.10.x" } }, "sha512-RnMvtDuR+68ig864gAvZCOdZehqhC5rFmMo0kn+ARfgVSTvFeF6IFLBVgMPUu0KwihaapEyW24WRi6nEyy1kSA=="],
"react-native-safe-area-context": ["react-native-safe-area-context@5.7.0", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-/9/MtQz8ODphjsLdZ+GZAIcC/RtoqW9EeShf7Uvnfgm/pzYrJ75y3PV/J1wuAV1T5Dye5ygq4EAW20RoBq0ABQ=="],
"react-native-screens": ["react-native-screens@4.26.2", "", { "dependencies": { "react-freeze": "^1.0.0", "warn-once": "^0.1.0" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-2XnWsZToKj76trGtEZzx5ELD/qOICFEprEeUntImmitQFVUkea27fiWdUSITArI356Y1qynpXZINW+Unbhky/A=="],
+ "react-native-svg": ["react-native-svg@15.15.4", "", { "dependencies": { "css-select": "^5.1.0", "css-tree": "^1.1.3", "warn-once": "0.1.1" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-boT/vIRgj6zZKBpfTPJJiYWMbZE9duBMOwPK6kCSTgxsS947IFMOq9OgIFkpWZTB7t229H24pDRkh3W9ZK/J1A=="],
+
"react-native-url-polyfill": ["react-native-url-polyfill@4.0.0", "", { "peerDependencies": { "react-native": "*" } }, "sha512-eqYM3wBAA0eL1sPYbBAoNfbES3+NkgcxUdelQ7QzmoVtqKB5qGG0U13MPTRUroAWK+y2EoJFS3MZUK0fwTf0pA=="],
"react-native-web": ["react-native-web@0.21.4", "", { "dependencies": { "@babel/runtime": "^7.18.6", "@react-native/normalize-colors": "^0.74.1", "fbjs": "^3.0.4", "inline-style-prefixer": "^7.0.1", "memoize-one": "^6.0.0", "nullthrows": "^1.1.1", "postcss-value-parser": "^4.2.0", "styleq": "^0.1.3" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-77B6KlalB4osNIck91TNFixPkYPsTbuNEvw8P7IV+4v4/5wZSBRKZnw0Zhx6YjuauDTz71gc8IW+fVhcBMjr5A=="],
@@ -1493,6 +1577,8 @@
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
+ "require-main-filename": ["require-main-filename@2.0.0", "", {}, "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="],
+
"resolve": ["resolve@2.0.0-next.7", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.2", "node-exports-info": "^1.6.0", "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ=="],
"resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="],
@@ -1527,6 +1613,8 @@
"server-only": ["server-only@0.0.1", "", {}, "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA=="],
+ "set-blocking": ["set-blocking@2.0.0", "", {}, "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="],
+
"set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
"set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="],
@@ -1617,6 +1705,10 @@
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
+ "synckit": ["synckit@0.11.13", "", { "dependencies": { "@pkgr/core": "^0.3.6" } }, "sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg=="],
+
+ "tagged-tag": ["tagged-tag@1.0.0", "", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="],
+
"tailwindcss": ["tailwindcss@4.1.12", "", {}, "sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA=="],
"tailwindcss-safe-area": ["tailwindcss-safe-area@1.3.0", "", { "peerDependencies": { "tailwindcss": "^4.0.0" } }, "sha512-RoxnW1zAjBWC3XK+row7Qj5toRMRlKNN/p3FLXb6fTGKxDGWT6JP/mcNX1yf09xRficQ308hbwiedgniepSp1Q=="],
@@ -1629,6 +1721,8 @@
"terser": ["terser@5.51.2", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-bWnjSNscmuI+GJze6ZupnHP8G/cTcsJF+bXCeQknk2SHQsgbNJnLrqiH9jZ2W4STPVXH2mDKKRX3iwPhc9Cn/Q=="],
+ "text-encoding": ["text-encoding@0.7.0", "", {}, "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA=="],
+
"throat": ["throat@5.0.0", "", {}, "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA=="],
"tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="],
@@ -1681,6 +1775,8 @@
"unicode-segmenter": ["unicode-segmenter@0.17.3", "", {}, "sha512-hKZwqBjJDmqNrq1+LjDxck1qLzJFcLLJM2Xq92ORRHOuau6GSHeELmn+uyk6zNH1CK9mogrUJGP9WJCJUQXMAw=="],
+ "unicorn-magic": ["unicorn-magic@0.3.0", "", {}, "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="],
+
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
"unrs-resolver": ["unrs-resolver@1.12.2", "", { "dependencies": { "napi-postinstall": "^0.3.4" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.12.2", "@unrs/resolver-binding-android-arm64": "1.12.2", "@unrs/resolver-binding-darwin-arm64": "1.12.2", "@unrs/resolver-binding-darwin-x64": "1.12.2", "@unrs/resolver-binding-freebsd-x64": "1.12.2", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", "@unrs/resolver-binding-linux-x64-musl": "1.12.2", "@unrs/resolver-binding-openharmony-arm64": "1.12.2", "@unrs/resolver-binding-wasm32-wasi": "1.12.2", "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" } }, "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ=="],
@@ -1731,6 +1827,8 @@
"which-collection": ["which-collection@1.0.2", "", { "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="],
+ "which-module": ["which-module@2.0.1", "", {}, "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ=="],
+
"which-typed-array": ["which-typed-array@1.1.24", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.9", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-wk4Mf4pR5mRP7eYuuTBCIQ9d0ud2Fv2jRLQpfgnRjbOxAFHmjKFValgTpitVKzJJS8ajnYQV2Du1SZ8j6b/EUQ=="],
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
@@ -1761,6 +1859,8 @@
"zod-validation-error": ["zod-validation-error@4.0.2", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ=="],
+ "zxing-wasm": ["zxing-wasm@3.1.3", "", { "dependencies": { "@types/emscripten": "^1.41.5", "type-fest": "^5.8.0" } }, "sha512-3lC9BJk4fR5ZJxcGjb0hVnDFOW7KpLXHIebiBmVd4FDRQZVeObztoTKxRUPxlWwSgxrMRONK0u50hBD1aTYEKg=="],
+
"@atproto-labs/simple-store-memory/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
"@atproto/lex-data/unicode-segmenter": ["unicode-segmenter@0.14.5", "", {}, "sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g=="],
@@ -1793,10 +1893,14 @@
"@expo/metro-config/hermes-parser": ["hermes-parser@0.36.1", "", { "dependencies": { "hermes-estree": "0.36.1" } }, "sha512-GApNk4zLHi2UWoWZZkx7LNCOSzLSc5lB55pZ/PhK7ycFeg7u5LcF88p/WbpIi1XUDtE0MpHE3uRR3u3KB7TjSQ=="],
+ "@expo/plist/@xmldom/xmldom": ["@xmldom/xmldom@0.8.15", "", {}, "sha512-/5NV/vDALVFDXgLmfsy9TRCBlKwO2LNBFzpzvb9iIj+jR+eSc6DLYYvVOdivT/jm7MtU6TebYuRmzEOI7w40UA=="],
+
"@expo/plist/xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="],
"@expo/ws-tunnel/ws": ["ws@8.22.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg=="],
+ "@react-native-vector-icons/common/find-up": ["find-up@8.0.0", "", { "dependencies": { "locate-path": "^8.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-JGG8pvDi2C+JxidYdIwQDyS/CgcrIdh18cvgxcBge3wSHRQOrooMD3GlFBcmMJAN9M42SAZjDp5zv1dglJjwww=="],
+
"@react-native/babel-preset/@react-native/babel-plugin-codegen": ["@react-native/babel-plugin-codegen@0.87.1", "", { "dependencies": { "@babel/traverse": "^7.29.0", "@react-native/codegen": "0.87.1" } }, "sha512-DfnyLAHG7jH4pqeID7ib6AeD6gG8T+KvM3c/w/yEa5ONkfRcHvTC8JMIRdhUqs4ruA+8xbYcNVQzewYRCPm1Xw=="],
"@react-native/babel-preset/babel-plugin-syntax-hermes-parser": ["babel-plugin-syntax-hermes-parser@0.36.1", "", { "dependencies": { "hermes-parser": "0.36.1" } }, "sha512-ycduwJbvdvIMmVvlAZqGggS+pm5Eu4Bk9pcV9Sm2Z4PJNRVsKkv0g7vHj+LeuC1gHTeF67sJXFOq61IlqCa2hA=="],
@@ -1839,6 +1943,8 @@
"connect/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
+ "css-tree/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
+
"eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
"eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
@@ -1851,6 +1957,8 @@
"eslint-plugin-react-hooks/hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="],
+ "expo/babel-preset-expo": ["babel-preset-expo@57.0.15", "", { "dependencies": { "@babel/generator": "^7.20.5", "@babel/helper-module-imports": "^7.25.9", "@babel/plugin-proposal-decorators": "^7.12.9", "@babel/plugin-proposal-export-default-from": "^7.24.7", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-default-from": "^7.24.7", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-transform-async-generator-functions": "^7.25.4", "@babel/plugin-transform-async-to-generator": "^7.24.7", "@babel/plugin-transform-block-scoping": "^7.25.0", "@babel/plugin-transform-class-properties": "^7.25.4", "@babel/plugin-transform-class-static-block": "^7.27.1", "@babel/plugin-transform-classes": "^7.25.4", "@babel/plugin-transform-destructuring": "^7.24.8", "@babel/plugin-transform-export-namespace-from": "^7.25.9", "@babel/plugin-transform-flow-strip-types": "^7.25.2", "@babel/plugin-transform-for-of": "^7.24.7", "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", "@babel/plugin-transform-modules-commonjs": "^7.24.8", "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", "@babel/plugin-transform-object-rest-spread": "^7.24.7", "@babel/plugin-transform-optional-catch-binding": "^7.24.7", "@babel/plugin-transform-optional-chaining": "^7.24.8", "@babel/plugin-transform-parameters": "^7.24.7", "@babel/plugin-transform-private-methods": "^7.24.7", "@babel/plugin-transform-private-property-in-object": "^7.24.7", "@babel/plugin-transform-react-display-name": "^7.24.7", "@babel/plugin-transform-react-jsx": "^7.28.6", "@babel/plugin-transform-react-jsx-development": "^7.27.1", "@babel/plugin-transform-react-pure-annotations": "^7.27.1", "@babel/plugin-transform-runtime": "^7.24.7", "@babel/plugin-transform-typescript": "^7.25.2", "@babel/plugin-transform-unicode-regex": "^7.24.7", "@babel/preset-typescript": "^7.23.0", "@react-native/babel-plugin-codegen": "0.86.3", "babel-plugin-react-compiler": "^1.0.0", "babel-plugin-react-native-web": "~0.21.0", "babel-plugin-syntax-hermes-parser": "^0.36.0", "babel-plugin-transform-flow-enums": "^0.0.2", "debug": "^4.3.4" }, "peerDependencies": { "@babel/runtime": "^7.20.0", "expo": "*", "expo-widgets": "^57.0.24", "react-refresh": ">=0.14.0 <1.0.0" }, "optionalPeers": ["@babel/runtime", "expo", "expo-widgets"] }, "sha512-GIJZeeBlDAO753yBaslD5wkElbB1//D0oRanc7ncahXDD0cBizIznJDNm1cKgVxcMaVZFacdozSa8jFHV1YJfQ=="],
+
"expo-modules-autolinking/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="],
"fbjs/promise": ["promise@7.3.1", "", { "dependencies": { "asap": "~2.0.3" } }, "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg=="],
@@ -1911,9 +2019,9 @@
"ora/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="],
- "path-scurry/lru-cache": ["lru-cache@11.5.3", "", {}, "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg=="],
+ "parse-png/pngjs": ["pngjs@3.4.0", "", {}, "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w=="],
- "plist/@xmldom/xmldom": ["@xmldom/xmldom@0.9.12", "", {}, "sha512-5AXjrcMClTryPe9LgZrygpB1lj7s0S9E0+W+AHaVKAVyHanafK86iPSvG5xHVSp/jC+VH1UXu0TAEmY279xH7A=="],
+ "path-scurry/lru-cache": ["lru-cache@11.5.3", "", {}, "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg=="],
"plist/xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="],
@@ -1923,6 +2031,8 @@
"prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
+ "qrcode/yargs": ["yargs@15.4.1", "", { "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" } }, "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A=="],
+
"react-native-css/babel-plugin-react-compiler": ["babel-plugin-react-compiler@19.1.0-rc.1-rc-af1b7da-20250421", "", { "dependencies": { "@babel/types": "^7.26.0" } }, "sha512-E3kaokBhWDLf7ZD8fuYjYn0ZJHYZ+3EHtAWCdX2hl4lpu1z9S/Xr99sxhx2bTCVB41oIesz9FtM8f4INsrZaOw=="],
"react-native-web/@react-native/normalize-colors": ["@react-native/normalize-colors@0.74.89", "", {}, "sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg=="],
@@ -1945,12 +2055,16 @@
"wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+ "zxing-wasm/type-fest": ["type-fest@5.10.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-NoSdpq/WEiAg5sjmBkmV/hfxv6HJH4NqPNrqjtSO5CwRmpsDfaf4begxW34KdJykH/l1yHtwBWQkCRdoXO8mPA=="],
+
"@expo/fingerprint/minimatch/brace-expansion": ["brace-expansion@5.0.12", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ=="],
"@expo/metro-config/hermes-parser/hermes-estree": ["hermes-estree@0.36.1", "", {}, "sha512-guv1nQ6IJ7S83NRFPWc3SA7IBZrdNC9kapwOq6uXvF4wP+sDCgjzQbKPCoyYmoyZRzztF/n/c36l/rccCZSiCw=="],
"@expo/metro/metro-source-map/ob1": ["ob1@0.84.5", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-aH9RkoZc7w/90HBamFxTw8ZLFr05wXS+iOnvmrgo53Ep8Pyrm5FieQSaPIVROkfFVQISeD/zo92fes26TOwe+A=="],
+ "@react-native-vector-icons/common/find-up/locate-path": ["locate-path@8.0.0", "", { "dependencies": { "p-locate": "^6.0.0" } }, "sha512-XT9ewWAC43tiAV7xDAPflMkG0qOPn2QjHqlgX8FOqmWa/rxnyYDulF9T0F7tRy1u+TVTmK/M//6VIOye+2zDXg=="],
+
"@react-native/babel-preset/@react-native/babel-plugin-codegen/@react-native/codegen": ["@react-native/codegen@0.87.1", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/parser": "^7.29.0", "hermes-parser": "0.36.1", "invariant": "^2.2.4", "nullthrows": "^1.1.1", "tinyglobby": "^0.2.15", "yargs": "^17.6.2" } }, "sha512-qbaqEdlUfj2vRgvWTpMoNgHnEqAhAYJLUrpGkb0WC9n0kdtqUvgigpz4bDktZwolM9BemXwgGFgyiAnbs3t0xw=="],
"@react-native/babel-preset/babel-plugin-syntax-hermes-parser/hermes-parser": ["hermes-parser@0.36.1", "", { "dependencies": { "hermes-estree": "0.36.1" } }, "sha512-GApNk4zLHi2UWoWZZkx7LNCOSzLSc5lB55pZ/PhK7ycFeg7u5LcF88p/WbpIi1XUDtE0MpHE3uRR3u3KB7TjSQ=="],
@@ -1971,6 +2085,8 @@
"eslint-plugin-react-hooks/hermes-parser/hermes-estree": ["hermes-estree@0.25.1", "", {}, "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw=="],
+ "expo/babel-preset-expo/babel-plugin-syntax-hermes-parser": ["babel-plugin-syntax-hermes-parser@0.36.1", "", { "dependencies": { "hermes-parser": "0.36.1" } }, "sha512-ycduwJbvdvIMmVvlAZqGggS+pm5Eu4Bk9pcV9Sm2Z4PJNRVsKkv0g7vHj+LeuC1gHTeF67sJXFOq61IlqCa2hA=="],
+
"finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
"glob/minimatch/brace-expansion": ["brace-expansion@5.0.12", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ=="],
@@ -2003,10 +2119,20 @@
"ora/chalk/supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="],
+ "qrcode/yargs/cliui": ["cliui@6.0.0", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="],
+
+ "qrcode/yargs/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
+
+ "qrcode/yargs/y18n": ["y18n@4.0.3", "", {}, "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="],
+
+ "qrcode/yargs/yargs-parser": ["yargs-parser@18.1.3", "", { "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" } }, "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ=="],
+
"send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
"@expo/fingerprint/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
+ "@react-native-vector-icons/common/find-up/locate-path/p-locate": ["p-locate@6.0.0", "", { "dependencies": { "p-limit": "^4.0.0" } }, "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw=="],
+
"@react-native/babel-preset/@react-native/babel-plugin-codegen/@react-native/codegen/hermes-parser": ["hermes-parser@0.36.1", "", { "dependencies": { "hermes-estree": "0.36.1" } }, "sha512-GApNk4zLHi2UWoWZZkx7LNCOSzLSc5lB55pZ/PhK7ycFeg7u5LcF88p/WbpIi1XUDtE0MpHE3uRR3u3KB7TjSQ=="],
"@react-native/babel-preset/babel-plugin-syntax-hermes-parser/hermes-parser/hermes-estree": ["hermes-estree@0.36.1", "", {}, "sha512-guv1nQ6IJ7S83NRFPWc3SA7IBZrdNC9kapwOq6uXvF4wP+sDCgjzQbKPCoyYmoyZRzztF/n/c36l/rccCZSiCw=="],
@@ -2035,6 +2161,8 @@
"@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
+ "expo/babel-preset-expo/babel-plugin-syntax-hermes-parser/hermes-parser": ["hermes-parser@0.36.1", "", { "dependencies": { "hermes-estree": "0.36.1" } }, "sha512-GApNk4zLHi2UWoWZZkx7LNCOSzLSc5lB55pZ/PhK7ycFeg7u5LcF88p/WbpIi1XUDtE0MpHE3uRR3u3KB7TjSQ=="],
+
"glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
"log-symbols/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
@@ -2045,6 +2173,16 @@
"ora/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="],
+ "qrcode/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+
+ "qrcode/yargs/cliui/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
+
+ "qrcode/yargs/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="],
+
+ "qrcode/yargs/yargs-parser/camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="],
+
+ "@react-native-vector-icons/common/find-up/locate-path/p-locate/p-limit": ["p-limit@4.0.0", "", { "dependencies": { "yocto-queue": "^1.0.0" } }, "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ=="],
+
"@react-native/babel-preset/@react-native/babel-plugin-codegen/@react-native/codegen/hermes-parser/hermes-estree": ["hermes-estree@0.36.1", "", {}, "sha512-guv1nQ6IJ7S83NRFPWc3SA7IBZrdNC9kapwOq6uXvF4wP+sDCgjzQbKPCoyYmoyZRzztF/n/c36l/rccCZSiCw=="],
"@react-native/metro-config/metro-config/metro/accepts/negotiator": ["negotiator@1.1.0", "", { "dependencies": { "content-type": "^2.1.0" } }, "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg=="],
@@ -2055,8 +2193,16 @@
"@react-native/metro-config/metro-config/metro/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
+ "expo/babel-preset-expo/babel-plugin-syntax-hermes-parser/hermes-parser/hermes-estree": ["hermes-estree@0.36.1", "", {}, "sha512-guv1nQ6IJ7S83NRFPWc3SA7IBZrdNC9kapwOq6uXvF4wP+sDCgjzQbKPCoyYmoyZRzztF/n/c36l/rccCZSiCw=="],
+
"log-symbols/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
"ora/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
+
+ "qrcode/yargs/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
+
+ "@react-native-vector-icons/common/find-up/locate-path/p-locate/p-limit/yocto-queue": ["yocto-queue@1.2.2", "", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="],
+
+ "qrcode/yargs/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
}
}
diff --git a/touchgrass.app/eslint.config.js b/touchgrass.app/eslint.config.js
index 9d86e68..2ace5eb 100644
--- a/touchgrass.app/eslint.config.js
+++ b/touchgrass.app/eslint.config.js
@@ -1,10 +1,16 @@
// https://docs.expo.dev/guides/using-eslint/
-import { defineConfig } from 'eslint/config';
-import expoConfig from 'eslint-config-expo/flat.js';
+import expoConfig from "eslint-config-expo/flat.js";
+import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
+import { defineConfig } from "eslint/config";
export default defineConfig([
expoConfig,
+ eslintPluginPrettierRecommended,
{
- ignores: ["dist/*"],
- }
+ ignores: [
+ "dist/*",
+ // auto generated
+ "src/lexicons/*",
+ ],
+ },
]);
diff --git a/touchgrass.app/lex.config.ts b/touchgrass.app/lex.config.ts
index f6bd4da..f15324a 100644
--- a/touchgrass.app/lex.config.ts
+++ b/touchgrass.app/lex.config.ts
@@ -1,12 +1,12 @@
-import { defineLexiconConfig } from '@atcute/lex-cli';
+import { defineLexiconConfig } from "@atcute/lex-cli";
export default defineLexiconConfig({
- generate: {
- files: ['lexicons-src/**/*.ts'],
- outdir: 'src/lexicons/',
- },
- export: {
- outdir: 'lexicons/',
- clean: true,
- },
-});
\ No newline at end of file
+ generate: {
+ files: ["lexicons-src/**/*.ts"],
+ outdir: "src/lexicons/",
+ },
+ export: {
+ outdir: "lexicons/",
+ clean: true,
+ },
+});
diff --git a/touchgrass.app/lexicons-src/gay/lunarequest/pubkey.ts b/touchgrass.app/lexicons-src/gay/lunarequest/pubkey.ts
index 64688f2..07d5287 100644
--- a/touchgrass.app/lexicons-src/gay/lunarequest/pubkey.ts
+++ b/touchgrass.app/lexicons-src/gay/lunarequest/pubkey.ts
@@ -1,17 +1,21 @@
-import { document, object, record, required, string } from '@atcute/lexicon-doc/builder';
+import { document, object, record, required, string } from "@atcute/lexicon-doc/builder";
export default document({
- id: "gay.lunarequest.touchgrass.pubkey",
- defs: {
- main: record({
- key: "any",
- record: object({
- properties: {
- PubKey: required(string({ description: "a pubkey used by the user" })),
- ValidTill: required(string({ format: "datetime", description: "the expiry of the pub key" })),
- CreatedAt: required(string({ format: "datetime", description: "when record was created", }))
- }
- })
- })
- }
-})
\ No newline at end of file
+ id: "gay.lunarequest.touchgrass.pubkey",
+ defs: {
+ main: record({
+ key: "any",
+ record: object({
+ properties: {
+ PubKey: required(string({ description: "a pubkey used by the user" })),
+ ValidTill: required(
+ string({ format: "datetime", description: "the expiry of the pub key" }),
+ ),
+ CreatedAt: required(
+ string({ format: "datetime", description: "when record was created" }),
+ ),
+ },
+ }),
+ }),
+ },
+});
diff --git a/touchgrass.app/lexicons-src/gay/lunarequest/touchedgrass.ts b/touchgrass.app/lexicons-src/gay/lunarequest/touchedgrass.ts
index 6a404ca..30d63b3 100644
--- a/touchgrass.app/lexicons-src/gay/lunarequest/touchedgrass.ts
+++ b/touchgrass.app/lexicons-src/gay/lunarequest/touchedgrass.ts
@@ -1,17 +1,24 @@
-import { document, object, record, required, string } from '@atcute/lexicon-doc/builder';
+import { document, object, record, required, string } from "@atcute/lexicon-doc/builder";
export default document({
- id: "gay.lunarequest.touchgrass.touchedgrass",
- defs: {
- main: record({
- key: "any",
- record: object({
- properties: {
- Target: required(string({ format: "did", description: "target for irl meetup, did of the user being verified" })),
- Payload: required(string({ description: "signed did of the person verifiying" })),
- CreatedAt: required(string({ format: "datetime", description: "when record was created", }))
- }
- })
- })
- }
-})
\ No newline at end of file
+ id: "gay.lunarequest.touchgrass.touchedgrass",
+ defs: {
+ main: record({
+ key: "any",
+ record: object({
+ properties: {
+ Target: required(
+ string({
+ format: "did",
+ description: "target for irl meetup, did of the user being verified",
+ }),
+ ),
+ Payload: required(string({ description: "signed did of the person verifiying" })),
+ CreatedAt: required(
+ string({ format: "datetime", description: "when record was created" }),
+ ),
+ },
+ }),
+ }),
+ },
+});
diff --git a/touchgrass.app/lexicons/gay/lunarequest/touchgrass/pubkey.json b/touchgrass.app/lexicons/gay/lunarequest/touchgrass/pubkey.json
index 139ccaa..468cace 100644
--- a/touchgrass.app/lexicons/gay/lunarequest/touchgrass/pubkey.json
+++ b/touchgrass.app/lexicons/gay/lunarequest/touchgrass/pubkey.json
@@ -19,11 +19,7 @@
"format": "datetime"
}
},
- "required": [
- "PubKey",
- "ValidTill",
- "CreatedAt"
- ],
+ "required": ["PubKey", "ValidTill", "CreatedAt"],
"type": "object"
},
"type": "record"
@@ -31,4 +27,4 @@
},
"id": "gay.lunarequest.touchgrass.pubkey",
"lexicon": 1
-}
\ No newline at end of file
+}
diff --git a/touchgrass.app/lexicons/gay/lunarequest/touchgrass/touchedgrass.json b/touchgrass.app/lexicons/gay/lunarequest/touchgrass/touchedgrass.json
index 2c0ac6a..6bb9a13 100644
--- a/touchgrass.app/lexicons/gay/lunarequest/touchgrass/touchedgrass.json
+++ b/touchgrass.app/lexicons/gay/lunarequest/touchgrass/touchedgrass.json
@@ -19,11 +19,7 @@
"format": "datetime"
}
},
- "required": [
- "Target",
- "Payload",
- "CreatedAt"
- ],
+ "required": ["Target", "Payload", "CreatedAt"],
"type": "object"
},
"type": "record"
@@ -31,4 +27,4 @@
},
"id": "gay.lunarequest.touchgrass.touchedgrass",
"lexicon": 1
-}
\ No newline at end of file
+}
diff --git a/touchgrass.app/metro.config.js b/touchgrass.app/metro.config.cjs
similarity index 81%
rename from touchgrass.app/metro.config.js
rename to touchgrass.app/metro.config.cjs
index cc0e83d..761bcb5 100644
--- a/touchgrass.app/metro.config.js
+++ b/touchgrass.app/metro.config.cjs
@@ -1,8 +1,7 @@
// Learn more https://docs.expo.io/guides/customizing-metro
-const { getDefaultConfig } = require('expo/metro-config');
+const { getDefaultConfig } = require("expo/metro-config");
const { withNativewind } = require("nativewind/metro");
-
/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);
diff --git a/touchgrass.app/nativewind-env.d.ts b/touchgrass.app/nativewind-env.d.ts
index 8197891..dde9c2c 100644
--- a/touchgrass.app/nativewind-env.d.ts
+++ b/touchgrass.app/nativewind-env.d.ts
@@ -1,5 +1,4 @@
///
///
-
-// NOTE: This file should not be edited and should be committed with your source code. It is generated by react-native-css. If you need to move or disable this file, please see the documentation.
\ No newline at end of file
+// NOTE: This file should not be edited and should be committed with your source code. It is generated by react-native-css. If you need to move or disable this file, please see the documentation.
diff --git a/touchgrass.app/package.json b/touchgrass.app/package.json
index 406edec..4eedfda 100644
--- a/touchgrass.app/package.json
+++ b/touchgrass.app/package.json
@@ -4,29 +4,31 @@
"version": "1.0.0",
"type": "module",
"dependencies": {
- "@atcute/atproto": "^4.0.4",
- "@atcute/client": "^5.1.2",
- "@atcute/identity": "^2",
- "@atcute/identity-resolver": "^2",
"@atcute/lex-cli": "^3.3.0",
"@atcute/lexicons": "^2",
"@atproto/api": "^0.24.3",
+ "@atproto/common-web": "^0.5.14",
"@atproto/oauth-client-expo": "^0.2.6",
- "@expo/ui": "~57.0.22",
+ "@expo/ui": "~57.0.23",
+ "@noble/post-quantum": "^0.7.1",
+ "@react-native-async-storage/async-storage": "2.2.0",
+ "@react-native-vector-icons/fontawesome6": "^13.1.5",
"@react-native/typescript-config": "^0.87.1",
"@types/node": "^26.6.4",
"core-js": "^3.50.0",
- "expo": "^57.0.27",
+ "expo": "~57.0.28",
+ "expo-blur": "~57.0.3",
+ "expo-camera": "~57.0.6",
"expo-constants": "~57.0.21",
- "expo-dev-client": "~57.0.19",
+ "expo-dev-client": "~57.0.20",
"expo-device": "~57.0.2",
"expo-font": "~57.0.4",
"expo-glass-effect": "~57.0.4",
"expo-image": "~57.0.5",
"expo-linking": "~57.0.12",
- "expo-router": "~57.0.25",
+ "expo-router": "~57.0.26",
"expo-secure-store": "~57.0.4",
- "expo-splash-screen": "~57.0.9",
+ "expo-splash-screen": "~57.0.10",
"expo-status-bar": "~57.0.1",
"expo-symbols": "~57.0.3",
"expo-system-ui": "~57.0.4",
@@ -36,12 +38,17 @@
"react-dom": "19.2.3",
"react-native": "0.86.3",
"react-native-css": "3.1.0-rc.0",
+ "react-native-device-info": "^15.0.2",
"react-native-gesture-handler": "~2.32.0",
+ "react-native-get-random-values": "~1.11.0",
"react-native-mmkv": "^4.3.2",
"react-native-nitro-modules": "^0.37.1",
+ "react-native-nitro-totp": "^2.0.1",
+ "react-native-qrcode-svg": "^6.3.26",
"react-native-reanimated": "4.5.1",
"react-native-safe-area-context": "~5.7.0",
"react-native-screens": "~4.26.0",
+ "react-native-svg": "15.15.4",
"react-native-url-polyfill": "^4.0.0",
"react-native-web": "~0.21.0",
"react-native-worklets": "0.10.1"
@@ -53,8 +60,11 @@
"babel-preset-expo": "^57.0.14",
"eslint": "^9",
"eslint-config-expo": "~57.0.2",
+ "eslint-config-prettier": "^10.1.8",
+ "eslint-plugin-prettier": "^5.5.6",
"lightningcss": "1.30.1",
"postcss": "8.5.29",
+ "prettier": "^3.9.10",
"prettier-plugin-tailwindcss": "^0.5.11",
"tailwindcss": "4.1.12",
"typescript": "~6.0.3"
@@ -62,6 +72,7 @@
"scripts": {
"start": "expo start",
"android": "expo run:android",
+ "android:prod": "eas build --platform android --local",
"ios": "expo run:ios",
"web": "expo start --web",
"lint": "expo lint",
@@ -69,7 +80,8 @@
"lexicons:export": "lex-cli export",
"lexicons:generate": "lex-cli generate",
"oauth-metadata": "bun scripts/sync-oauth-client-metadata.ts",
- "prebuild": "EXPO_NO_GIT_STATUS=1 expo prebuild --clean"
+ "prebuild": "EXPO_NO_GIT_STATUS=1 expo prebuild --clean",
+ "fmt": "prettier . --write"
},
"private": true,
"overrides": {
diff --git a/touchgrass.app/postcss.config.mjs b/touchgrass.app/postcss.config.mjs
index fe1e17e..c2ddf74 100644
--- a/touchgrass.app/postcss.config.mjs
+++ b/touchgrass.app/postcss.config.mjs
@@ -2,4 +2,4 @@ export default {
plugins: {
"@tailwindcss/postcss": {},
},
-};
\ No newline at end of file
+};
diff --git a/touchgrass.app/src/app/(authenticated)/_layout.tsx b/touchgrass.app/src/app/(authenticated)/_layout.tsx
deleted file mode 100644
index 7a0cd61..0000000
--- a/touchgrass.app/src/app/(authenticated)/_layout.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import { Stack } from "expo-router";
-
-export default function AuthenticatedLayout() {
- return (
-
- );
-}
diff --git a/touchgrass.app/src/app/(authenticated)/home.tsx b/touchgrass.app/src/app/(authenticated)/home.tsx
deleted file mode 100644
index 67fdf94..0000000
--- a/touchgrass.app/src/app/(authenticated)/home.tsx
+++ /dev/null
@@ -1,134 +0,0 @@
-import { useEffect, useState } from "react";
-import {
- ActivityIndicator,
- Pressable,
- StyleSheet,
- Text,
- View,
-} from "react-native";
-
-import { useAuth } from "../../components/SessionProvider";
-
-type SessionInfo = {
- did: string;
- handle: string;
- active?: boolean;
-};
-
-export default function Home() {
- const { agent, did, signOut } = useAuth();
-
- const [info, setInfo] = useState(null);
- const [error, setError] = useState(null);
-
- useEffect(() => {
- if (!agent) return;
-
- let cancelled = false;
-
- agent.com.atproto.server
- .getSession()
- .then((response) => {
- if (!cancelled) setInfo(response.data);
- })
- .catch((err: unknown) => {
- if (!cancelled)
- setError(err instanceof Error ? err.message : String(err));
- });
-
- return () => {
- cancelled = true;
- };
- }, [agent]);
-
- return (
-
- signed in as
-
- {info ? `@${info.handle}` : (did ?? "…")}
-
- {did}
-
- {error ? (
- {error}
- ) : info ? (
- com.atproto.server.getSession ok
- ) : (
-
- )}
-
-
-
- {
- void signOut();
- }}
- style={({ pressed }) => [
- styles.button,
- pressed && styles.buttonPressed,
- ]}
- >
- Sign out
-
-
- );
-}
-
-const styles = StyleSheet.create({
- container: {
- flex: 1,
- justifyContent: "center",
- paddingHorizontal: 24,
- },
- eyebrow: {
- fontSize: 12,
- letterSpacing: 1,
- textTransform: "uppercase",
- color: "#737373",
- },
- handle: {
- marginTop: 4,
- fontSize: 28,
- fontWeight: "700",
- color: "#ffffff",
- },
- meta: {
- marginTop: 6,
- fontSize: 12,
- color: "#a3a3a3",
- },
- ok: {
- marginTop: 20,
- fontSize: 13,
- color: "#4ade80",
- },
- error: {
- marginTop: 20,
- fontSize: 13,
- color: "#f87171",
- },
- spinner: {
- marginTop: 20,
- alignSelf: "flex-start",
- },
- spacer: {
- height: 32,
- },
- button: {
- borderWidth: 1,
- borderColor: "#262626",
- backgroundColor: "#171717",
- borderRadius: 16,
- paddingVertical: 16,
- alignItems: "center",
- },
- buttonPressed: {
- opacity: 0.7,
- },
- buttonLabel: {
- fontSize: 16,
- fontWeight: "600",
- color: "#ffffff",
- },
-});
diff --git a/touchgrass.app/src/app/(authenticated)/logout.tsx b/touchgrass.app/src/app/(authenticated)/logout.tsx
new file mode 100644
index 0000000..68f9167
--- /dev/null
+++ b/touchgrass.app/src/app/(authenticated)/logout.tsx
@@ -0,0 +1,5 @@
+import { View } from "react-native";
+
+export default function Logout() {
+ return ;
+}
diff --git a/touchgrass.app/src/app/(authenticated)/scan.tsx b/touchgrass.app/src/app/(authenticated)/scan.tsx
new file mode 100644
index 0000000..e955c1c
--- /dev/null
+++ b/touchgrass.app/src/app/(authenticated)/scan.tsx
@@ -0,0 +1,14 @@
+import { useAuth } from "#/components/SessionProvider";
+import { useRouter } from "expo-router";
+import { View } from "react-native";
+
+export default function Scan() {
+ const router = useRouter();
+ const { isAuthed } = useAuth();
+
+ if (!isAuthed) {
+ router.navigate("/sign-in");
+ }
+
+ return ;
+}
diff --git a/touchgrass.app/src/app/_layout.tsx b/touchgrass.app/src/app/_layout.tsx
index 8535d76..590a4e5 100644
--- a/touchgrass.app/src/app/_layout.tsx
+++ b/touchgrass.app/src/app/_layout.tsx
@@ -14,15 +14,12 @@ export default function RootLayout() {
}
function RootNavigator() {
- const { isLoading, isAuthed } = useAuth();
+ const { isLoading } = useAuth();
useEffect(() => {
- console.log(`loading: ${isLoading}`);
if (!isLoading) void SplashScreen.hideAsync();
}, [isLoading]);
- console.log(`authenticated ${isAuthed}`);
-
return (
-
-
-
-
-
-
-
-
+ />
);
}
diff --git a/touchgrass.app/src/app/about.tsx b/touchgrass.app/src/app/about.tsx
new file mode 100644
index 0000000..9a39d28
--- /dev/null
+++ b/touchgrass.app/src/app/about.tsx
@@ -0,0 +1,33 @@
+import { View, Text, Linking } from "react-native";
+import { SafeAreaView } from "react-native-safe-area-context";
+
+export default function About() {
+ return (
+
+
+ About
+
+
+
+
+ Touch Fluffy Tail originally started life as touchgrass. Unfortunately, the domains were
+ way too expensive, so it was renamed to Touch Fluffy Tail. The idea behind this app is a
+ way to "prove" that you met someone at a convention by having a record they have
+ attested to show you met them. Touch Fluffy Tail is mainly developed by 2 people{" "}
+ Linking.openURL("https://witchsky.app/profile/nullrequest.com")}>
+ Luna
+ {" "}
+ and{" "}
+ Linking.openURL("https://witchsky.app/profile/sakurakat.systems")}>
+ Kathryn
+
+ . It is however opensource and we are always open to contributions.
+
+
+
+ );
+}
diff --git a/touchgrass.app/src/app/index.tsx b/touchgrass.app/src/app/index.tsx
index 76d440c..443e576 100644
--- a/touchgrass.app/src/app/index.tsx
+++ b/touchgrass.app/src/app/index.tsx
@@ -1,11 +1,93 @@
-import { Redirect } from "expo-router";
-import { useAuth } from "../components/SessionProvider";
+import NavBar from "#/components/NavBar";
+import { AuthRequiredError, useAuth } from "#/components/SessionProvider";
+import { BlurTargetView, BlurView } from "expo-blur";
+import { router } from "expo-router";
+import { styled } from "nativewind";
+import { useRef, useState } from "react";
+import { Pressable, Text, View } from "react-native";
+import "react-native-get-random-values";
+import QRcode from "#/components/QRcode";
+import { SafeAreaView } from "react-native-safe-area-context";
import "./global.css";
-export default function Index() {
- const { isLoading, isAuthed } = useAuth();
+type VerifiedSession = {
+ handle: string;
+ did: string;
+};
- if (isLoading) return null;
+export default function Home() {
+ const { did, status, isAuthed, hasSavedDid, requireSession } = useAuth();
- return ;
+ const [busy, setBusy] = useState<"session" | "sign-out" | null>(null);
+ const [verified, setVerified] = useState(null);
+ const [notice, setNotice] = useState(null);
+ const [error, setError] = useState(null);
+
+ const qrTarget = useRef(null);
+
+ function report(error: unknown) {
+ setError(error instanceof Error ? error.message : String(error));
+ }
+
+ async function handleCheckSession() {
+ if (busy) return;
+
+ setBusy("session");
+ setError(null);
+ setNotice(null);
+
+ try {
+ const liveAgent = await requireSession();
+ const { data } = await liveAgent.com.atproto.server.getSession();
+
+ setVerified({ handle: data.handle, did: data.did });
+ setNotice("session verified");
+ } catch (sessionError) {
+ if (sessionError instanceof AuthRequiredError && sessionError.reason === "no-session") {
+ router.push("/sign-in");
+ return;
+ }
+
+ report(sessionError);
+ } finally {
+ setBusy(null);
+ }
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+ {!isAuthed ? (
+
+
+
+ router.push("/sign-in")}
+ className="items-center justify-center rounded-2xl border border-neutral-800 bg-neutral-900 px-8 py-4 active:opacity-70">
+ sign in to get started
+
+
+ ) : null}
+
+
+
+
+ );
}
+
+const StyledBlurTargetView = styled(BlurTargetView);
+const StyledBlurView = styled(BlurView);
diff --git a/touchgrass.app/src/app/sign-in.tsx b/touchgrass.app/src/app/sign-in.tsx
index dbd2a4d..bc6fb07 100644
--- a/touchgrass.app/src/app/sign-in.tsx
+++ b/touchgrass.app/src/app/sign-in.tsx
@@ -1,24 +1,17 @@
import OauthForm from "#/components/OAuthForm";
import { useAuth } from "#/components/SessionProvider";
-import { useState } from "react";
-import { Pressable, Text, View } from "react-native";
+import { mainSchema } from "#/lexicons/types/gay/lunarequest/touchgrass/pubkey";
+import { safeParse } from "@atcute/lexicons";
+import { Agent } from "@atproto/api";
+import { TID } from "@atproto/common-web";
+import { ml_dsa65 } from "@noble/post-quantum/ml-dsa.js";
+import { FontAwesome6 } from "@react-native-vector-icons/fontawesome6";
+import { router } from "expo-router";
+import { Text, View } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
-type LoginMode = "oauth" | "legacy";
-
-type SignInProps = {
- setError?: (v: string) => void;
-};
-
-export default function SignIn({ setError = () => {} }: SignInProps) {
+export default function SignIn() {
const { isLoading, signIn } = useAuth();
- const [mode, setMode] = useState("oauth");
-
- const switchMode = (next: LoginMode) => {
- if (next === mode) return;
- setError("");
- setMode(next);
- };
if (isLoading) return null;
@@ -27,57 +20,56 @@ export default function SignIn({ setError = () => {} }: SignInProps) {
- Sign In
+ {" "}
+ Sign In {" "}
-
- switchMode("oauth")}
- className={`flex-1 items-center justify-center border-b-2 py-4 ${
- mode === "oauth" ? "border-b-emerald-500" : "border-b-transparent"
- }`}
- >
-
- OAuth
-
-
-
- switchMode("legacy")}
- className={`flex-1 items-center justify-center border-b-2 py-4 ${
- mode === "legacy"
- ? "border-b-emerald-500"
- : "border-b-transparent"
- }`}
- >
-
- Legacy
-
-
-
+ {
+ await generate_key_pair(handle, signIn);
- {mode === "oauth" ? (
-
- ) : (
- Place holder for legacy login
- )}
+ if (router.canGoBack()) router.back();
+ else router.replace("/");
+ }}
+ />
);
}
+
+async function generate_key_pair(
+ handle: string,
+ signin: (handle: string, privateKey: Uint8Array) => Promise,
+) {
+ const keys = ml_dsa65.keygen();
+ const pubkey = keys.publicKey;
+ const privkey = keys.secretKey;
+
+ const agent = await signin(handle, privkey);
+ const createdAt = new Date().toISOString();
+ const validTill = new Date(createdAt);
+ validTill.setFullYear(validTill.getFullYear() + 1);
+ const tid = TID.nextStr();
+
+ const record = {
+ $type: "gay.lunarequest.touchgrass.pubkey",
+ CreatedAt: createdAt,
+ pubkey: Buffer.from(pubkey).toString("base64"),
+ ValidTill: validTill,
+ };
+
+ const result = safeParse(mainSchema, record);
+ if (!result.ok) {
+ throw new Error("How the fuck did we get here luna?");
+ }
+ const resp = await agent.com.atproto.repo.putRecord({
+ repo: agent.assertDid,
+ rkey: tid,
+ collection: "gay.lunarequest.touchgrass.pubkey",
+ record: record,
+ });
+ if (!resp.success) {
+ throw new Error("once more i thought this was not possible");
+ }
+}
diff --git a/touchgrass.app/src/components/BurgerMenu.tsx b/touchgrass.app/src/components/BurgerMenu.tsx
new file mode 100644
index 0000000..9cd48d7
--- /dev/null
+++ b/touchgrass.app/src/components/BurgerMenu.tsx
@@ -0,0 +1,51 @@
+import { FontAwesome6 } from "@react-native-vector-icons/fontawesome6";
+import { useState } from "react";
+import { Pressable, Text, View } from "react-native";
+
+export interface MenuItems {
+ title: string;
+ onpress: () => void;
+}
+
+interface HamburgerMenuProps {
+ items: MenuItems[];
+}
+
+export default function HamburgerMenu({ items }: HamburgerMenuProps) {
+ const [menuVisible, setMenuVisible] = useState(false);
+
+ return (
+
+ setMenuVisible((visible) => !visible)}
+ className="self-start"
+ accessibilityRole="button"
+ accessibilityLabel="Toggle menu"
+ accessibilityState={{ expanded: menuVisible }}>
+
+
+
+ {menuVisible && (
+
+ {items.map((item) => (
+ {
+ setMenuVisible(false);
+ item.onpress();
+ }}
+ className="my-1 rounded-md px-2.5 py-1 active:bg-[#2a2a3b]">
+ {item.title}
+
+ ))}
+
+ )}
+
+ );
+}
diff --git a/touchgrass.app/src/components/NavBar.tsx b/touchgrass.app/src/components/NavBar.tsx
new file mode 100644
index 0000000..2f127fa
--- /dev/null
+++ b/touchgrass.app/src/components/NavBar.tsx
@@ -0,0 +1,48 @@
+import { FontAwesome6 } from "@react-native-vector-icons/fontawesome6";
+import { useRouter } from "expo-router";
+import { Pressable, Text, View } from "react-native";
+import HamburgerMenu, { MenuItems } from "./BurgerMenu";
+import { useAuth } from "./SessionProvider";
+
+export default function NavBar() {
+ const router = useRouter();
+ const { isAuthed } = useAuth();
+
+ const items: MenuItems[] = [
+ ...(isAuthed
+ ? [
+ {
+ title: "Logout",
+ onpress: () => router.navigate("./(authenticated)/logout"),
+ },
+ ]
+ : []),
+ {
+ title: "About",
+ onpress: () => router.navigate("./about"),
+ },
+ ];
+
+ return (
+
+ Touch Fluffy Tail
+
+
+ router.navigate("./(authenticated)/scan")}
+ accessibilityRole="button"
+ accessibilityLabel="Switch camera view">
+
+
+
+
+
+
+ );
+}
diff --git a/touchgrass.app/src/components/OAuthForm.tsx b/touchgrass.app/src/components/OAuthForm.tsx
index 873d8f0..2e53d14 100644
--- a/touchgrass.app/src/components/OAuthForm.tsx
+++ b/touchgrass.app/src/components/OAuthForm.tsx
@@ -1,26 +1,173 @@
-import { useState } from "react";
-import { Pressable, Text, TextInput, View } from "react-native";
+import { useEffect, useRef, useState } from "react";
+import { ActivityIndicator, Pressable, Text, TextInput, View } from "react-native";
+import { HandleResult } from "./common/types";
+import TypeAheadResults from "./TypeAheadResults";
+
+const TYPEAHEAD_URL = "https://typeahead.waow.tech";
type OAuthFormProps = {
- signIn?: (v: string) => Promise;
+ signIn: (v: string) => Promise;
};
export default function OAuthForm({ signIn }: OAuthFormProps) {
const [handle, setHandle] = useState("");
+ const [results, setResults] = useState([]);
+ const [searching, setSearching] = useState(false);
+ const [showResults, setShowResults] = useState(false);
+ const [signingIn, setSigningIn] = useState(false);
+ const [error, setError] = useState(null);
+
+ const inputRef = useRef(null);
+ const requestId = useRef(0);
+
+ function handleQueryChange(value: string) {
+ setHandle(value);
+
+ if (value.trim().length < 2) {
+ // Invalidate any pending request.
+ requestId.current += 1;
+
+ setResults([]);
+ setShowResults(false);
+ setSearching(false);
+ }
+ }
+
+ useEffect(() => {
+ const query = handle.trim();
+
+ if (query.length < 2) return;
+
+ const currentRequestId = ++requestId.current;
+
+ const timeout = setTimeout(async () => {
+ setSearching(true);
+
+ try {
+ const response = await fetch(
+ `${TYPEAHEAD_URL}/xrpc/app.bsky.actor.searchActorsTypeahead?q=${encodeURIComponent(query)}&limit=10`,
+ {
+ headers: { "X-Client": "gay.lunrequest.touchgrass" },
+ },
+ );
+
+ if (!response.ok) {
+ throw new Error(`Typeahead request failed: ${response.status}`);
+ }
+
+ const data = await response.json();
+
+ if (currentRequestId !== requestId.current) return;
+
+ const actors: HandleResult[] = (data.actors ?? []).map(
+ (actor: { did: string; handle: string; displayName?: string; avatar?: string }) => ({
+ did: actor.did,
+ handle: actor.handle,
+ display_name: actor.displayName ?? actor.handle,
+ avatar_url: actor.avatar ?? null,
+ }),
+ );
+
+ setResults(actors);
+ setShowResults(actors.length > 0);
+ } catch (error) {
+ if (currentRequestId === requestId.current) {
+ console.error("Handle search failed:", error);
+ setResults([]);
+ setShowResults(false);
+ }
+ } finally {
+ if (currentRequestId === requestId.current) {
+ setSearching(false);
+ }
+ }
+ }, 300);
+
+ return () => clearTimeout(timeout);
+ }, [handle]);
+
+ function selectHandle(result: HandleResult) {
+ requestId.current += 1;
+
+ setHandle(result.handle);
+ setResults([]);
+ setShowResults(false);
+ setSearching(false);
+ inputRef.current?.blur();
+ }
+
+ async function handleLogin() {
+ const selectedHandle = handle.trim();
+
+ if (!selectedHandle || signingIn) return;
+
+ setSigningIn(true);
+ setError(null);
+
+ try {
+ await signIn(selectedHandle);
+ } catch (signInError) {
+ setError(signInError instanceof Error ? signInError.message : String(signInError));
+ } finally {
+ setSigningIn(false);
+ }
+ }
+
return (
-
-
- @
-
+
+
+
+ @
+
+ {
+ if (results.length > 0) setShowResults(true);
+ }}
+ onSubmitEditing={() => {
+ if (results.length === 1) {
+ selectHandle(results[0]);
+ }
+ }}
+ autoCapitalize="none"
+ autoCorrect={false}
+ spellCheck={false}
+ autoComplete="off"
+ returnKeyType="search"
+ accessibilityLabel="Search Bluesky handles"
+ />
+
+ {searching && }
+
+
+ {showResults && results.length > 0 && (
+
+ )}
-
- Login
+
+ {error && (
+
+ {error}
+
+ )}
+
+
+
+ {signingIn ? "Logging in..." : "Login"}
+
);
diff --git a/touchgrass.app/src/components/QRcode.tsx b/touchgrass.app/src/components/QRcode.tsx
new file mode 100644
index 0000000..4dcb94a
--- /dev/null
+++ b/touchgrass.app/src/components/QRcode.tsx
@@ -0,0 +1,124 @@
+import { deviceName } from "expo-device";
+import { useEffect, useState } from "react";
+import { ActivityIndicator, Text, View } from "react-native";
+import { NitroTotp, parseSecretKey } from "react-native-nitro-totp";
+import QRCode from "react-native-qrcode-svg";
+import { useAuth } from "./SessionProvider";
+
+const totp = new NitroTotp();
+const PERIOD = 30;
+
+type TotpResult = {
+ seed: string;
+ code: string;
+};
+
+export default function QRcode() {
+ const { seed } = useAuth();
+
+ const [totpResult, setTotpResult] = useState(null);
+
+ const [remaining, setRemaining] = useState(PERIOD);
+ const [macId] = useState("Unavailable");
+ const [error, setError] = useState(null);
+
+ const code = seed && totpResult?.seed === seed ? totpResult.code : "";
+
+ useEffect(() => {
+ if (!seed) return;
+
+ let active = true;
+
+ const generate = () => {
+ try {
+ const secret = parseSecretKey(seed);
+ const nextCode = totp.generate(secret);
+
+ if (active) {
+ setTotpResult({ seed, code: nextCode });
+ setError(null);
+ }
+ } catch {
+ if (active) {
+ setError("Unable to generate TOTP. Check the seed.");
+ }
+ }
+ };
+
+ const updateTimer = () => {
+ const seconds = Math.floor(Date.now() / 1000);
+ const left = PERIOD - (seconds % PERIOD);
+
+ if (active) {
+ setRemaining(left);
+ }
+
+ if (left === PERIOD) {
+ generate();
+ }
+ };
+
+ generate();
+ updateTimer();
+
+ const timer = setInterval(updateTimer, 250);
+
+ return () => {
+ active = false;
+ clearInterval(timer);
+ };
+ }, [seed]);
+
+ if (!seed) {
+ return (
+
+ No seed available.
+
+ );
+ }
+
+ const qrValue = code
+ ? JSON.stringify({
+ deviceName,
+ macId,
+ totp: code,
+ })
+ : "";
+ const progress = (PERIOD - remaining) / PERIOD;
+
+ return (
+
+
+ Verify Me :3
+
+ {error ? (
+ {error}
+ ) : code ? (
+ <>
+ Refreshes in {remaining}s
+
+
+
+
+
+
+
+ >
+ ) : (
+
+ )}
+
+
+ Device name
+
+
+ {deviceName ? deviceName : "Unknown device"}
+
+
+
+
+ );
+}
diff --git a/touchgrass.app/src/components/SessionProvider.tsx b/touchgrass.app/src/components/SessionProvider.tsx
index 799f390..3e999c4 100644
--- a/touchgrass.app/src/components/SessionProvider.tsx
+++ b/touchgrass.app/src/components/SessionProvider.tsx
@@ -1,101 +1,189 @@
import { oauthClient } from "#/lib/atproto";
import { Agent } from "@atproto/api";
+import { TokenRefreshError } from "@atproto/oauth-client-expo";
import { deleteItemAsync, getItemAsync, setItemAsync } from "expo-secure-store";
-import {
- createContext,
- ReactNode,
- useCallback,
- useContext,
- useEffect,
- useState,
-} from "react";
+import { createContext, ReactNode, useCallback, useContext, useEffect, useState } from "react";
+import { NitroSecret, SecretSize } from "react-native-nitro-totp";
const DID_KEY = "gay.lunarequest.touchgrass.did";
+const PRIVATE_KEY = "gay.lunarequest.touchgrass.privkey";
+const SEED_KEY = "gay.lunarequest.touchgrass.otp_seed";
+
+const nitroSecret = new NitroSecret();
+
+export type AuthRequiredReason = "no-session" | "unavailable";
+export class AuthRequiredError extends Error {
+ readonly reason: AuthRequiredReason;
+
+ constructor(reason: AuthRequiredReason, message: string, options?: ErrorOptions) {
+ super(message, options);
+ this.name = "AuthRequiredError";
+ this.reason = reason;
+ }
+}
+export type AuthStatus = "loading" | "anonymous" | "saved" | "authenticated";
type AuthContextType = {
agent: Agent | null;
did: string | null;
+ status: AuthStatus;
isLoading: boolean;
isAuthed: boolean;
- signIn: (handle: string) => Promise;
+ hasSavedDid: boolean;
+ seed: string | null;
+ privateKey: Uint8Array | null;
+ signIn: (handleOrDid: string, privateKey: Uint8Array) => Promise;
signOut: () => Promise;
+ requireSession: () => Promise;
};
const AuthContext = createContext(undefined);
+function toAuthRequiredError(error: unknown): AuthRequiredError {
+ if (error instanceof TokenRefreshError && error.cause === undefined) {
+ return new AuthRequiredError("no-session", "No usable session is stored on this device.", {
+ cause: error,
+ });
+ }
+
+ return new AuthRequiredError(
+ "unavailable",
+ error instanceof Error ? error.message : String(error),
+ { cause: error },
+ );
+}
+
export function AuthProvider({ children }: { children: ReactNode }) {
const [agent, setAgent] = useState(null);
const [did, setDid] = useState(null);
const [isLoading, setIsLoading] = useState(true);
-
- const setSession = useCallback(async (session: any) => {
- // i don't like using any but atproto can't do types right
- const nextAgent = new Agent(session);
-
- // the return shouldn't be reachable but it makes typescript's linter happy
- if (!session.did) {
- return;
- }
-
- await setItemAsync(DID_KEY, session.did);
- setDid(session.did);
- setAgent(nextAgent);
- }, []);
+ const [seed, setSeed] = useState(null);
+ const [privateKey, setPrivateKey] = useState | null>(null);
useEffect(() => {
let active = true;
- async function restoreSession() {
+
+ const loadSavedData = async () => {
try {
const savedDid = await getItemAsync(DID_KEY);
- if (!savedDid) return;
-
- const session = await oauthClient.restore(savedDid);
-
- if (active) {
- setDid(session.did);
- setAgent(new Agent(session));
+ if (active && savedDid) setDid(savedDid);
+ if (active && savedDid) {
+ const privateKeyString = await getItemAsync(PRIVATE_KEY);
+ if (!privateKeyString) {
+ throw new Error("Unable to find privatekey");
+ }
+ const pkey = new Uint8Array(Buffer.from(privateKeyString, "hex"));
+ setPrivateKey(pkey);
}
} catch (error) {
- console.error("Failed to restore AT Protocol session:", error);
- await deleteItemAsync(DID_KEY);
+ console.error("Failed to read the saved DID:", error);
} finally {
- if (active) setIsLoading(false);
+ const savedSeed = await getItemAsync(SEED_KEY);
+ if (active) {
+ if (savedSeed) {
+ setSeed(savedSeed);
+ } else {
+ let secret = nitroSecret.generate({ size: SecretSize.EXTENDED });
+ await setItemAsync(SEED_KEY, secret);
+ }
+ }
}
- }
- restoreSession();
+ if (active) setIsLoading(false);
+ };
+ void loadSavedData();
return () => {
active = false;
};
}, []);
+ const forgetIdentity = useCallback(async () => {
+ if (did) {
+ try {
+ await oauthClient.revoke(did);
+ } catch (error) {
+ console.warn("Failed to revoke the AT Protocol session:", error);
+ }
+ }
+
+ await deleteItemAsync(DID_KEY);
+ await deleteItemAsync(PRIVATE_KEY);
+ await deleteItemAsync(SEED_KEY);
+ }, [did]);
+
+ const requireSession = useCallback(async (): Promise => {
+ if (agent) return agent;
+
+ if (!did) {
+ throw new AuthRequiredError("no-session", "No account is saved on this device.");
+ }
+
+ try {
+ const session = await oauthClient.restore(did);
+ const nextAgent = new Agent(session);
+
+ setAgent(nextAgent);
+
+ return nextAgent;
+ } catch (error) {
+ console.error("Failed to restore the AT Protocol session:", error);
+
+ throw toAuthRequiredError(error);
+ }
+ }, [agent, did]);
+
const signIn = useCallback(
- async (handle: string) => {
- const session = await oauthClient.signIn(handle);
+ async (handleOrDid: string, privateKey: Uint8Array): Promise => {
+ const session = await oauthClient.signIn(handleOrDid.trim());
+
+ if (did && did !== session.did) await forgetIdentity();
+
+ await setItemAsync(DID_KEY, session.did);
+ setDid(session.did);
- await setSession(session);
+ const privateKeyString = Buffer.from(privateKey).toString("hex");
+ await setItemAsync("PRIVATE_KEY", privateKeyString);
+ setPrivateKey(privateKey);
+
+ const nextAgent = new Agent(session);
+ setAgent(nextAgent);
+
+ return nextAgent;
},
- [setSession],
+ [did, forgetIdentity],
);
const signOut = useCallback(async () => {
- await deleteItemAsync(DID_KEY);
+ await forgetIdentity();
setAgent(null);
setDid(null);
- }, []);
+ setPrivateKey(null);
+ }, [forgetIdentity]);
+
+ const status: AuthStatus = isLoading
+ ? "loading"
+ : agent
+ ? "authenticated"
+ : did
+ ? "saved"
+ : "anonymous";
return (
+ requireSession,
+ }}>
{children}
);
@@ -105,7 +193,7 @@ export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
- throw new Error("useAtprotoAuth must be used inside AtprotoProvider");
+ throw new Error("useAuth must be used inside AuthProvider");
}
return context;
diff --git a/touchgrass.app/src/components/TypeAheadResults.tsx b/touchgrass.app/src/components/TypeAheadResults.tsx
new file mode 100644
index 0000000..d506eed
--- /dev/null
+++ b/touchgrass.app/src/components/TypeAheadResults.tsx
@@ -0,0 +1,50 @@
+import { FlatList, Image, Pressable, Text, View } from "react-native";
+import { HandleResult } from "./common/types";
+type Props = {
+ results: HandleResult[];
+ selectHandle: (item: HandleResult) => void;
+};
+
+export default function TypeAheadResults({ results, selectHandle }: Props) {
+ return (
+
+ item.did}
+ renderItem={({ item, index }) => (
+ selectHandle(item)}
+ className={`flex-row items-center gap-3 px-3 py-3 ${
+ index !== results.length - 1 ? "border-b border-emerald-800" : ""
+ }`}>
+ {item.avatar_url ? (
+
+ ) : (
+
+ )}
+
+
+
+ {item.display_name}
+
+
+
+ @{item.handle}
+
+
+
+ )}
+ nestedScrollEnabled={true}
+ scrollEnabled={true}
+ keyboardShouldPersistTaps="always"
+ showsVerticalScrollIndicator={true}
+ bounces={false}
+ style={{ maxHeight: 240, flexGrow: 0 }}
+ />
+
+ );
+}
diff --git a/touchgrass.app/src/components/common/types.ts b/touchgrass.app/src/components/common/types.ts
new file mode 100644
index 0000000..790f676
--- /dev/null
+++ b/touchgrass.app/src/components/common/types.ts
@@ -0,0 +1,8 @@
+interface HandleResult {
+ did: string;
+ handle: string;
+ display_name: string;
+ avatar_url: string | null;
+}
+
+export type { HandleResult };
diff --git a/touchgrass.app/src/lexicons/types/gay/lunarequest/touchgrass/pubkey.ts b/touchgrass.app/src/lexicons/types/gay/lunarequest/touchgrass/pubkey.ts
index 83c4950..debe655 100644
--- a/touchgrass.app/src/lexicons/types/gay/lunarequest/touchgrass/pubkey.ts
+++ b/touchgrass.app/src/lexicons/types/gay/lunarequest/touchgrass/pubkey.ts
@@ -1,26 +1,24 @@
-import type {} from '@atcute/lexicons';
-import * as v from '@atcute/lexicons/validations';
-import type {} from '@atcute/lexicons/ambient';
+import type {} from "@atcute/lexicons";
+import * as v from "@atcute/lexicons/validations";
+import type {} from "@atcute/lexicons/ambient";
const _mainSchema = /*#__PURE__*/ v.record(
- /*#__PURE__*/ v.string(),
- /*#__PURE__*/ v.object(
- {
- "$type": /*#__PURE__*/ v.literal("gay.lunarequest.touchgrass.pubkey"),
- /**
- * when record was created
- */
- "CreatedAt": /*#__PURE__*/ v.datetimeString(),
- /**
- * a pubkey used by the user
- */
- "PubKey": /*#__PURE__*/ v.string(),
- /**
- * the expiry of the pub key
- */
- "ValidTill": /*#__PURE__*/ v.datetimeString(),
- }
- )
+ /*#__PURE__*/ v.string(),
+ /*#__PURE__*/ v.object({
+ $type: /*#__PURE__*/ v.literal("gay.lunarequest.touchgrass.pubkey"),
+ /**
+ * when record was created
+ */
+ CreatedAt: /*#__PURE__*/ v.datetimeString(),
+ /**
+ * a pubkey used by the user
+ */
+ PubKey: /*#__PURE__*/ v.string(),
+ /**
+ * the expiry of the pub key
+ */
+ ValidTill: /*#__PURE__*/ v.datetimeString(),
+ }),
);
type main$schematype = typeof _mainSchema;
@@ -28,8 +26,8 @@ export interface mainSchema extends main$schematype {}
export const mainSchema = _mainSchema as mainSchema;
export interface Main extends v.InferInput {}
-declare module '@atcute/lexicons/ambient' {
- interface Records {
- "gay.lunarequest.touchgrass.pubkey": mainSchema;
- }
+declare module "@atcute/lexicons/ambient" {
+ interface Records {
+ "gay.lunarequest.touchgrass.pubkey": mainSchema;
+ }
}
diff --git a/touchgrass.app/src/lexicons/types/gay/lunarequest/touchgrass/touchedgrass.ts b/touchgrass.app/src/lexicons/types/gay/lunarequest/touchgrass/touchedgrass.ts
index 1155b7e..4942390 100644
--- a/touchgrass.app/src/lexicons/types/gay/lunarequest/touchgrass/touchedgrass.ts
+++ b/touchgrass.app/src/lexicons/types/gay/lunarequest/touchgrass/touchedgrass.ts
@@ -1,26 +1,24 @@
-import type {} from '@atcute/lexicons';
-import * as v from '@atcute/lexicons/validations';
-import type {} from '@atcute/lexicons/ambient';
+import type {} from "@atcute/lexicons";
+import * as v from "@atcute/lexicons/validations";
+import type {} from "@atcute/lexicons/ambient";
const _mainSchema = /*#__PURE__*/ v.record(
- /*#__PURE__*/ v.string(),
- /*#__PURE__*/ v.object(
- {
- "$type": /*#__PURE__*/ v.literal("gay.lunarequest.touchgrass.touchedgrass"),
- /**
- * when record was created
- */
- "CreatedAt": /*#__PURE__*/ v.datetimeString(),
- /**
- * signed did of the person verifiying
- */
- "Payload": /*#__PURE__*/ v.string(),
- /**
- * target for irl meetup, did of the user being verified
- */
- "Target": /*#__PURE__*/ v.didString(),
- }
- )
+ /*#__PURE__*/ v.string(),
+ /*#__PURE__*/ v.object({
+ $type: /*#__PURE__*/ v.literal("gay.lunarequest.touchgrass.touchedgrass"),
+ /**
+ * when record was created
+ */
+ CreatedAt: /*#__PURE__*/ v.datetimeString(),
+ /**
+ * signed did of the person verifiying
+ */
+ Payload: /*#__PURE__*/ v.string(),
+ /**
+ * target for irl meetup, did of the user being verified
+ */
+ Target: /*#__PURE__*/ v.didString(),
+ }),
);
type main$schematype = typeof _mainSchema;
@@ -28,8 +26,8 @@ export interface mainSchema extends main$schematype {}
export const mainSchema = _mainSchema as mainSchema;
export interface Main extends v.InferInput {}
-declare module '@atcute/lexicons/ambient' {
- interface Records {
- "gay.lunarequest.touchgrass.touchedgrass": mainSchema;
- }
+declare module "@atcute/lexicons/ambient" {
+ interface Records {
+ "gay.lunarequest.touchgrass.touchedgrass": mainSchema;
+ }
}
diff --git a/touchgrass.app/src/lib/atproto.ts b/touchgrass.app/src/lib/atproto.ts
index cc74a00..592d705 100644
--- a/touchgrass.app/src/lib/atproto.ts
+++ b/touchgrass.app/src/lib/atproto.ts
@@ -1,9 +1,8 @@
-import { ExpoOAuthClient } from '@atproto/oauth-client-expo';
+import { ExpoOAuthClient } from "@atproto/oauth-client-expo";
import clientMetadata from "../../assets/oauth-client-metadata.json";
-
export const oauthClient = new ExpoOAuthClient({
- handleResolver: "https://bsky.social", // look into a alternative for this and let people choose maybe?,
- // @ts-expect-error: TS2322 there's some weirdness with converting json to types
- clientMetadata
-})
\ No newline at end of file
+ handleResolver: "https://bsky.social", // look into a alternative for this and let people choose maybe?,
+ // @ts-expect-error: TS2322 there's some weirdness with converting json to types
+ clientMetadata,
+});
diff --git a/touchgrass.app/tsconfig.json b/touchgrass.app/tsconfig.json
index 121e6e5..ed60bab 100644
--- a/touchgrass.app/tsconfig.json
+++ b/touchgrass.app/tsconfig.json
@@ -3,51 +3,24 @@
"compilerOptions": {
"jsx": "react-jsx",
"module": "esnext",
- "moduleSuffixes": [
- ".ios",
- ".android",
- ".native",
- ""
- ],
- "lib": [
- "dom",
- "esnext"
- ],
- "types": [
- "node"
- ],
+ "moduleSuffixes": [".ios", ".android", ".native", ""],
+ "lib": ["dom", "esnext"],
+ "types": ["node"],
"paths": {
- "#/*": [
- "./src/*"
- ],
- "crypto": [
- "./src/platform/crypto.ts"
- ],
- "expo-file-system/build/*": [
- "./node_modules/expo-file-system/build/*"
- ]
+ "#/*": ["./src/*"],
+ "crypto": ["./src/platform/crypto.ts"],
+ "expo-file-system/build/*": ["./node_modules/expo-file-system/build/*"]
},
"plugins": [
{
"name": "ts-plugin-sort-import-suggestions",
- "moveUpPatterns": [
- "#/",
- "@lingui/react/macro"
- ],
- "moveDownPatterns": [
- "react-native-reanimated/lib"
- ]
+ "moveUpPatterns": ["#/", "@lingui/react/macro"],
+ "moveDownPatterns": ["react-native-reanimated/lib"]
}
],
- "customConditions": [
- "react-native"
- ]
+ "customConditions": ["react-native"]
},
- "exclude": [
- "bskyweb",
- "bskyembed",
- "web-build"
- ],
+ "exclude": ["bskyweb", "bskyembed", "web-build"],
"include": [
"**/*.ts",
"**/*.tsx",
@@ -56,4 +29,4 @@
"lex.config.mts",
"nativewind-env.d.ts"
]
-}
\ No newline at end of file
+}