From 6dbf70d3bd1fec69088dc74756c44938efdd9aeb Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Apr 2026 05:02:57 +0000 Subject: [PATCH 01/14] Update package-lock.json with resolved dependency tree https://claude.ai/code/session_01MQS5o6eJUphagSBZKMQD1w --- package-lock.json | 323 +++++++++++++++++++++++----------------------- 1 file changed, 160 insertions(+), 163 deletions(-) diff --git a/package-lock.json b/package-lock.json index a3bbe99..b9e5ea8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@expo/vector-icons": "^15.0.3", "@react-native-async-storage/async-storage": "2.2.0", - "@react-native-picker/picker": "2.11.1", + "@react-native-picker/picker": "2.11.4", "@types/tesseract.js": "^0.0.2", "expo": "~54.0.0", "expo-blur": "~15.0.8", @@ -23,18 +23,19 @@ "expo-linear-gradient": "~15.0.8", "expo-location": "~19.0.8", "expo-notifications": "~0.32.16", + "expo-secure-store": "~15.0.5", "expo-status-bar": "~3.0.9", "react": "19.1.0", "react-native": "0.81.5", "react-native-android-widget": "^0.20.1", "react-native-calendars": "^1.1314.0", - "react-native-webview": "13.15.0", + "react-native-webview": "13.16.1", "tesseract.js": "^7.0.0" }, "devDependencies": { "@react-native-community/cli": "^20.1.3", "@types/react": "~19.1.10", - "pdfjs-dist": "^5.5.207", + "pdfjs-dist": "^5.6.205", "typescript": "~5.9.2" } }, @@ -80,7 +81,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1492,7 +1492,6 @@ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -1885,14 +1884,14 @@ "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause" }, "node_modules/@hapi/topo": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause", "dependencies": { "@hapi/hoek": "^9.0.0" @@ -2379,7 +2378,7 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", @@ -2393,7 +2392,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -2403,7 +2402,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -2429,9 +2428,8 @@ "version": "20.1.3", "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-20.1.3.tgz", "integrity": "sha512-sLo8cu9JyFNfuuF1C+8NJ4DHE/PEFaXGd4enkcxi/OJjGG8+sOQrdjNQ4i+cVh/2c+ah1mEMwsYjc3z0+/MqSg==", - "devOptional": true, + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@react-native-community/cli-clean": "20.1.3", "@react-native-community/cli-config": "20.1.3", @@ -2460,7 +2458,7 @@ "version": "20.1.3", "resolved": "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-20.1.3.tgz", "integrity": "sha512-sFLdLzapfC0scjgzBJJWYDY2RhHPjuuPkA5r6q0gc/UQH/izXpMpLrhh1DW84cMDraNACK0U62tU7ebNaQ1LMQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@react-native-community/cli-tools": "20.1.3", @@ -2473,7 +2471,7 @@ "version": "20.1.3", "resolved": "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-20.1.3.tgz", "integrity": "sha512-n73nW0cG92oNF0r994pPqm0DjAShOm3F8LSffDYhJqNAno+h/csmv/37iL4NtSpmKIO8xqsG3uVTXz9X/hzNaQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@react-native-community/cli-tools": "20.1.3", @@ -2488,7 +2486,7 @@ "version": "20.1.3", "resolved": "https://registry.npmjs.org/@react-native-community/cli-config-android/-/cli-config-android-20.1.3.tgz", "integrity": "sha512-DNHDP+OWLyhKShGciBqPcxhxfp1Z/7GQcb4F+TGyCeKQAr+JdnUjRXN3X+YCU/v+g2kbYYyRJKlGabzkVvdrAw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@react-native-community/cli-tools": "20.1.3", @@ -2501,7 +2499,7 @@ "version": "20.1.3", "resolved": "https://registry.npmjs.org/@react-native-community/cli-config-apple/-/cli-config-apple-20.1.3.tgz", "integrity": "sha512-QX9B83nAfCPs0KiaYz61kAEHWr9sttooxzRzNdQwvZTwnsIpvWOT9GvMMj/19OeXiQzMJBzZX0Pgt6+spiUsDQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@react-native-community/cli-tools": "20.1.3", @@ -2514,7 +2512,7 @@ "version": "20.1.3", "resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-20.1.3.tgz", "integrity": "sha512-EI+mAPWn255/WZ4CQohy1I049yiaxVr41C3BeQ2BCyhxODIDR8XRsLzYb1t9MfqK/C3ZncUN2mPSRXFeKPPI1w==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@react-native-community/cli-config": "20.1.3", @@ -2538,7 +2536,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "restore-cursor": "^3.1.0" @@ -2551,7 +2549,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.1.0", @@ -2568,7 +2566,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -2578,7 +2576,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" @@ -2594,7 +2592,7 @@ "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "bl": "^4.1.0", @@ -2618,7 +2616,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "onetime": "^5.1.0", @@ -2632,7 +2630,7 @@ "version": "20.1.3", "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-20.1.3.tgz", "integrity": "sha512-bzB9ELPOISuqgtDZXFPQlkuxx1YFkNx3cNgslc5ElCrk+5LeCLQLIBh/dmIuK8rwUrPcrramjeBj++Noc+TaAA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@react-native-community/cli-config-android": "20.1.3", @@ -2646,7 +2644,7 @@ "version": "20.1.3", "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-apple/-/cli-platform-apple-20.1.3.tgz", "integrity": "sha512-XJ+DqAD4hkplWVXK5AMgN7pP9+4yRSe5KfZ/b42+ofkDBI55ALlUmX+9HWE3fMuRjcotTCoNZqX2ov97cFDXpQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@react-native-community/cli-config-apple": "20.1.3", @@ -2660,7 +2658,7 @@ "version": "20.1.3", "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-20.1.3.tgz", "integrity": "sha512-2qL48SINotuHbZO73cgqSwqd/OWNx0xTbFSdujhpogV4p8BNwYYypfjh4vJY5qJEB5PxuoVkMXT+aCADpg9nBg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@react-native-community/cli-platform-apple": "20.1.3" @@ -2670,7 +2668,7 @@ "version": "20.1.3", "resolved": "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-20.1.3.tgz", "integrity": "sha512-hsNsdUKZDd2T99OuNuiXz4VuvLa1UN0zcxefmPjXQgI0byrBLzzDr+o7p03sKuODSzKi2h+BMnUxiS07HACQLA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@react-native-community/cli-tools": "20.1.3", @@ -2690,7 +2688,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -2700,7 +2698,7 @@ "version": "6.4.0", "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "is-wsl": "^1.1.0" @@ -2713,7 +2711,7 @@ "version": "6.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "async-limiter": "~1.0.0" @@ -2723,7 +2721,7 @@ "version": "20.1.3", "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-20.1.3.tgz", "integrity": "sha512-EAn0vPCMxtHhfWk2UwLmSUfPfLUnFgC7NjiVJVTKJyVk5qGnkPfoT8te/1IUXFTysUB0F0RIi+NgDB4usFOLeA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@vscode/sudo-prompt": "^9.0.0", @@ -2742,7 +2740,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "restore-cursor": "^3.1.0" @@ -2755,7 +2753,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "locate-path": "^6.0.0", @@ -2772,7 +2770,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "p-locate": "^5.0.0" @@ -2788,7 +2786,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.1.0", @@ -2805,7 +2803,7 @@ "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "devOptional": true, + "dev": true, "license": "MIT", "bin": { "mime": "cli.js" @@ -2818,7 +2816,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -2828,7 +2826,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" @@ -2844,7 +2842,7 @@ "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "bl": "^4.1.0", @@ -2868,7 +2866,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" @@ -2884,7 +2882,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "p-limit": "^3.0.2" @@ -2900,7 +2898,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "onetime": "^5.1.0", @@ -2914,7 +2912,7 @@ "version": "20.1.3", "resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-20.1.3.tgz", "integrity": "sha512-IdAcegf0pH1hVraxWTG1ACLkYC0LDQfqtaEf42ESyLIF3Xap70JzL/9tAlxw7lSCPZPFWhrcgU0TBc4SkC/ecw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "joi": "^17.2.1" @@ -2924,7 +2922,7 @@ "version": "9.5.0", "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": "^12.20.0 || >=14" @@ -2934,7 +2932,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "locate-path": "^6.0.0", @@ -2951,7 +2949,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "p-locate": "^5.0.0" @@ -2967,7 +2965,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" @@ -2983,7 +2981,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "p-limit": "^3.0.2" @@ -2996,9 +2994,9 @@ } }, "node_modules/@react-native-picker/picker": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/@react-native-picker/picker/-/picker-2.11.1.tgz", - "integrity": "sha512-ThklnkK4fV3yynnIIRBkxxjxR4IFbdMNJVF6tlLdOJ/zEFUEFUEdXY0KmH0iYzMwY8W4/InWsLiA7AkpAbnexA==", + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@react-native-picker/picker/-/picker-2.11.4.tgz", + "integrity": "sha512-Kf8h1AMnBo54b1fdiVylP2P/iFcZqzpMYcglC28EEFB1DEnOjsNr6Ucqc+3R9e91vHxEDnhZFbYDmAe79P2gjA==", "license": "MIT", "workspaces": [ "example" @@ -3266,7 +3264,7 @@ "version": "4.1.5", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause", "dependencies": { "@hapi/hoek": "^9.0.0" @@ -3276,14 +3274,14 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause" }, "node_modules/@sideway/pinpoint": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause" }, "node_modules/@sinclair/typebox": { @@ -3397,9 +3395,8 @@ "version": "19.1.17", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.17.tgz", "integrity": "sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA==", - "devOptional": true, + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -3467,7 +3464,7 @@ "version": "9.3.2", "resolved": "https://registry.npmjs.org/@vscode/sudo-prompt/-/sudo-prompt-9.3.2.tgz", "integrity": "sha512-gcXoCN00METUNFeQOFJ+C9xUI0DKB+0EGMVg7wbVYRHBw2Eq3fKisDZOkRdOz3kqXRKOENMfShPOmypw1/8nOw==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/@xmldom/xmldom": { @@ -3562,7 +3559,7 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/ansi-fragments/-/ansi-fragments-0.2.1.tgz", "integrity": "sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "colorette": "^1.0.7", @@ -3574,7 +3571,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -3584,7 +3581,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^4.1.0" @@ -3652,7 +3649,7 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/appdirsjs/-/appdirsjs-1.2.7.tgz", "integrity": "sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/arg": { @@ -3693,7 +3690,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -3996,7 +3993,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "buffer": "^5.5.0", @@ -4014,7 +4011,7 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "bytes": "^3.1.2", @@ -4039,7 +4036,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "ee-first": "1.1.1" @@ -4109,7 +4106,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -4223,7 +4219,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4393,14 +4389,14 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/command-exists": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/commander": { @@ -4506,7 +4502,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -4535,7 +4531,7 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz", "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "env-paths": "^2.2.1", @@ -4562,14 +4558,14 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "devOptional": true, + "dev": true, "license": "Python-2.0" }, "node_modules/cosmiconfig/node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -4596,14 +4592,14 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/dayjs": { "version": "1.11.20", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz", "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/debug": { @@ -4627,7 +4623,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -4815,7 +4811,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4825,7 +4821,7 @@ "version": "7.21.0", "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.21.0.tgz", "integrity": "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==", - "devOptional": true, + "dev": true, "license": "MIT", "bin": { "envinfo": "dist/cli.js" @@ -4838,7 +4834,7 @@ "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" @@ -4857,7 +4853,7 @@ "version": "1.5.2", "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.2.tgz", "integrity": "sha512-kNAL7hESndBCrWwS72QyV3IVOTrVmj9D062FV5BQswNL5zEdeRmz/WJFyh6Aj/plvvSOrzddkxW57HgkZcR9Fw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "accepts": "~1.3.8", @@ -4963,7 +4959,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", @@ -4987,7 +4983,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4997,7 +4993,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" @@ -5014,7 +5010,6 @@ "resolved": "https://registry.npmjs.org/expo/-/expo-54.0.33.tgz", "integrity": "sha512-3yOEfAKqo+gqHcV8vKcnq0uA5zxlohnhA3fu4G43likN8ct5ZZ3LjAh9wDdKteEkoad3tFPvwxmXW711S5OHUw==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.20.0", "@expo/cli": "54.0.23", @@ -5130,7 +5125,6 @@ "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-14.0.11.tgz", "integrity": "sha512-ga0q61ny4s/kr4k8JX9hVH69exVSIfcIc19+qZ7gt71Mqtm7xy2c6kwsPTCyhBW2Ro5yXTT8EaZOpuRi35rHbg==", "license": "MIT", - "peer": true, "dependencies": { "fontfaceobserver": "^2.1.0" }, @@ -5239,6 +5233,15 @@ "react-native": "*" } }, + "node_modules/expo-secure-store": { + "version": "15.0.8", + "resolved": "https://registry.npmjs.org/expo-secure-store/-/expo-secure-store-15.0.8.tgz", + "integrity": "sha512-lHnzvRajBu4u+P99+0GEMijQMFCOYpWRO4dWsXSuMt77+THPIGjzNvVKrGSl6mMrLsfVaKL8BpwYZLGlgA+zAw==", + "license": "MIT", + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-server": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/expo-server/-/expo-server-1.0.5.tgz", @@ -5547,7 +5550,7 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -5570,7 +5573,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz", "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -5586,7 +5589,7 @@ "version": "5.5.9", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.9.tgz", "integrity": "sha512-jldvxr1MC6rtiZKgrFnDSvT8xuH+eJqxqOBThUVjYrxssYTo1avZLGql5l0a0BAERR01CadYzZ83kVEkbyDg+g==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -5607,7 +5610,7 @@ "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "devOptional": true, + "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -5746,7 +5749,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", @@ -5863,7 +5866,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -5902,7 +5905,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "devOptional": true, + "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -6119,7 +6122,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=10.17.0" @@ -6129,7 +6132,7 @@ "version": "0.7.2", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -6196,7 +6199,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "parent-module": "^1.0.0", @@ -6213,7 +6216,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -6280,7 +6283,7 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/is-callable": { @@ -6329,7 +6332,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -6367,7 +6370,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -6380,7 +6383,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6442,7 +6445,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6470,7 +6473,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -6732,7 +6735,7 @@ "version": "17.13.3", "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause", "dependencies": { "@hapi/hoek": "^9.3.0", @@ -6783,7 +6786,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/json5": { @@ -6802,7 +6805,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "devOptional": true, + "dev": true, "license": "MIT", "optionalDependencies": { "graceful-fs": "^4.1.6" @@ -6830,7 +6833,7 @@ "version": "2.13.2", "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.13.2.tgz", "integrity": "sha512-4VVDnbOpLXy/s8rdRCSXb+zfMeFR0WlJWpET1iA9CQdlZDfwyLjUuGQzXU4VeOoey6AicSAluWan7Etga6Kcmg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "picocolors": "^1.1.1", @@ -7243,7 +7246,7 @@ "version": "0.7.1", "resolved": "https://registry.npmjs.org/logkitty/-/logkitty-0.7.1.tgz", "integrity": "sha512-/3ER20CTTbahrCrpYfPn7Xavv9diBROZpoXGVZDWMw4b/X4uuUwAC0ki85tgsdMRONURyIJbcOvS94QsUBYPbQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "ansi-fragments": "^0.2.1", @@ -7258,7 +7261,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -7268,7 +7271,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "devOptional": true, + "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -7280,7 +7283,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -7295,14 +7298,14 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "devOptional": true, + "dev": true, "license": "ISC" }, "node_modules/logkitty/node_modules/yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "cliui": "^6.0.0", @@ -7325,7 +7328,7 @@ "version": "18.1.3", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "devOptional": true, + "dev": true, "license": "ISC", "dependencies": { "camelcase": "^5.0.0", @@ -7384,7 +7387,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -7418,7 +7421,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -7881,7 +7884,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/nocache/-/nocache-3.0.4.tgz", "integrity": "sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -7940,7 +7943,7 @@ "version": "1.15.0", "resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.15.0.tgz", "integrity": "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -7978,7 +7981,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.0.0" @@ -8018,7 +8021,7 @@ "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -8288,7 +8291,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "callsites": "^3.0.0" @@ -8301,7 +8304,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", @@ -8350,7 +8353,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.2.0.tgz", "integrity": "sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -8412,16 +8415,16 @@ } }, "node_modules/pdfjs-dist": { - "version": "5.5.207", - "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.5.207.tgz", - "integrity": "sha512-WMqqw06w1vUt9ZfT0gOFhMf3wHsWhaCrxGrckGs5Cci6ybDW87IvPaOd2pnBwT6BJuP/CzXDZxjFgmSULLdsdw==", + "version": "5.6.205", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.6.205.tgz", + "integrity": "sha512-tlUj+2IDa7G1SbvBNN74UHRLJybZDWYom+k6p5KIZl7huBvsA4APi6mKL+zCxd3tLjN5hOOEE9Tv7VdzO88pfg==", "dev": true, "license": "Apache-2.0", "engines": { "node": ">=20.19.0 || >=22.13.0 || >=24" }, "optionalDependencies": { - "@napi-rs/canvas": "^0.1.95", + "@napi-rs/canvas": "^0.1.96", "node-readable-to-web-readable-stream": "^0.4.2" } }, @@ -8436,7 +8439,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -8629,7 +8631,7 @@ "version": "6.15.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -8654,7 +8656,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -8684,7 +8686,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "bytes": "~3.1.2", @@ -8716,7 +8718,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -8742,7 +8743,6 @@ "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.81.5.tgz", "integrity": "sha512-1w+/oSjEXZjMqsIvmkCRsOc8UBYv163bTWKTI8+1mxztvQPhCRYGTvZ/PL1w16xXHneIj/SLGfxWg2GWN2uexw==", "license": "MIT", - "peer": true, "dependencies": { "@jest/create-cache-key-function": "^29.7.0", "@react-native/assets-registry": "0.81.5", @@ -8849,11 +8849,10 @@ "license": "MIT" }, "node_modules/react-native-webview": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.15.0.tgz", - "integrity": "sha512-Vzjgy8mmxa/JO6l5KZrsTC7YemSdq+qB01diA0FqjUTaWGAGwuykpJ73MDj3+mzBSlaDxAEugHzTtkUQkQEQeQ==", + "version": "13.16.1", + "resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.16.1.tgz", + "integrity": "sha512-If0eHhoEdOYDcHsX+xBFwHMbWBGK1BvGDQDQdVkwtSIXiq1uiqjkpWVP2uQ1as94J0CzvFE9PUNDuhiX0Z6ubw==", "license": "MIT", - "peer": true, "dependencies": { "escape-string-regexp": "^4.0.0", "invariant": "2.2.4" @@ -8952,7 +8951,6 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -8961,7 +8959,7 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "inherits": "^2.0.3", @@ -9068,7 +9066,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "devOptional": true, + "dev": true, "license": "ISC" }, "node_modules/requireg": { @@ -9154,7 +9152,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -9224,7 +9222,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -9285,7 +9283,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/sax": { @@ -9421,7 +9419,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "devOptional": true, + "dev": true, "license": "ISC" }, "node_modules/set-function-length": { @@ -9484,7 +9482,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -9504,7 +9502,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -9521,7 +9519,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -9540,7 +9538,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -9604,7 +9602,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^3.2.0", @@ -9619,7 +9617,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^1.9.0" @@ -9632,7 +9630,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "color-name": "1.1.3" @@ -9642,14 +9640,14 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -9768,14 +9766,14 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/strict-url-sanitise/-/strict-url-sanitise-0.0.1.tgz", "integrity": "sha512-nuFtF539K8jZg3FjaWH/L8eocCR6gegz5RDOsaWxfdbF5Jqr2VXWxZayjTwUzsWJDC91k2EbnJXp6FuWW+Z4hg==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" @@ -9811,7 +9809,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -9830,7 +9828,7 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.2.tgz", "integrity": "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -10169,7 +10167,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "content-type": "^1.0.5", @@ -10184,7 +10182,7 @@ "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -10194,7 +10192,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "mime-db": "^1.54.0" @@ -10213,7 +10211,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10281,7 +10278,7 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 4.0.0" @@ -10343,7 +10340,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/utils-merge": { @@ -10476,7 +10473,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "devOptional": true, + "dev": true, "license": "ISC" }, "node_modules/which-typed-array": { From 47d0237df9a8c0d24384d2629be138a770358e99 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Apr 2026 05:35:34 +0000 Subject: [PATCH 02/14] Add Android SDK installer artifacts to .gitignore https://claude.ai/code/session_01MQS5o6eJUphagSBZKMQD1w --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index a198171..2f5567d 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,9 @@ test_cli.js # generated native folders /ios .dual-graph/ + +# android sdk installer artifacts +*.deb +debian-binary +control.tar.xz +data.tar.xz From 849d78010848bba16cf24cdfc1c90482f244a4c7 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Apr 2026 11:27:44 +0000 Subject: [PATCH 03/14] chore: make gradlew executable and ignore gradle artifacts https://claude.ai/code/session_01MQS5o6eJUphagSBZKMQD1w --- .gitignore | 1 + android/gradlew | 0 2 files changed, 1 insertion(+) mode change 100644 => 100755 android/gradlew diff --git a/.gitignore b/.gitignore index 2f5567d..358f005 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,4 @@ test_cli.js debian-binary control.tar.xz data.tar.xz +org/ diff --git a/android/gradlew b/android/gradlew old mode 100644 new mode 100755 From dd105369e117b4acad146cfb20b633e55745c9a3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Apr 2026 16:25:19 +0000 Subject: [PATCH 04/14] feat: add GitHub Actions workflow to build AeroStaffPro APK Adds build-apk.yml workflow that: - Triggers on push to build branch or manual dispatch - Sets up Java 21, Android SDK, NDK 27.1.12297006, build-tools 36 - Applies patch for expo-modules-core shouldIncludeCompose Gradle bug - Builds debug APK with ./gradlew :app:assembleDebug - Uploads APK as downloadable artifact https://claude.ai/code/session_01MQS5o6eJUphagSBZKMQD1w --- .github/workflows/build-apk.yml | 66 +++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 .github/workflows/build-apk.yml diff --git a/.github/workflows/build-apk.yml b/.github/workflows/build-apk.yml new file mode 100644 index 0000000..b8ada5b --- /dev/null +++ b/.github/workflows/build-apk.yml @@ -0,0 +1,66 @@ +name: Build APK + +on: + workflow_dispatch: + push: + branches: + - claude/build-aerostaffpro-apk-R00yT + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 21 + + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + + - name: Install NDK + run: | + echo "y" | sdkmanager "ndk;27.1.12297006" "build-tools;36.0.0" "platforms;android-36" + + - name: Install dependencies + run: npm ci --legacy-peer-deps + + - name: Fix expo-modules-core shouldIncludeCompose bug + run: | + # The buildscript.ext properties are not accessible in the android {} block + # in newer Gradle versions. Re-declare at project level after the buildscript block. + sed -i "s|apply plugin: 'com.android.library'|// Fix: re-evaluate at project level so android {} block can access it\ndef _coreFeatures = project.findProperty(\"coreFeatures\") ?: []\next.shouldIncludeCompose = _coreFeatures.contains(\"compose\")\n\napply plugin: 'com.android.library'|" \ + node_modules/expo-modules-core/android/build.gradle + # Also change 'compose shouldIncludeCompose' to 'compose = shouldIncludeCompose' (property setter syntax) + sed -i 's/^ compose shouldIncludeCompose$/ compose = shouldIncludeCompose/' \ + node_modules/expo-modules-core/android/build.gradle + + - name: Build debug APK + run: | + cd android + export ANDROID_HOME=$ANDROID_SDK_ROOT + ./gradlew :app:assembleDebug --no-daemon --stacktrace 2>&1 + env: + ANDROID_SDK_ROOT: ${{ env.ANDROID_SDK_ROOT }} + GRADLE_OPTS: -Xmx4g -XX:MaxMetaspaceSize=512m + + - name: Find APK + run: find android -name "*.apk" -type f + + - name: Upload APK artifact + uses: actions/upload-artifact@v4 + with: + name: AeroStaffPro-debug + path: android/app/build/outputs/apk/debug/*.apk + if-no-files-found: error From d48ffceda496f5a8d888c71dcad68275cf186d08 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Apr 2026 16:38:37 +0000 Subject: [PATCH 05/14] fix: improve build-apk workflow with CMake, better patch, verbose output - Add cmake;3.22.1 to sdkmanager installs - Use more robust regex for compose setter patch - Add --info flag to Gradle for better error visibility - Show last 200 lines of build output - Always run Find APK step https://claude.ai/code/session_01MQS5o6eJUphagSBZKMQD1w --- .github/workflows/build-apk.yml | 45 ++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build-apk.yml b/.github/workflows/build-apk.yml index b8ada5b..13d5b39 100644 --- a/.github/workflows/build-apk.yml +++ b/.github/workflows/build-apk.yml @@ -29,34 +29,49 @@ jobs: - name: Setup Android SDK uses: android-actions/setup-android@v3 - - name: Install NDK + - name: Install SDK components run: | - echo "y" | sdkmanager "ndk;27.1.12297006" "build-tools;36.0.0" "platforms;android-36" + echo "y" | sdkmanager \ + "ndk;27.1.12297006" \ + "build-tools;36.0.0" \ + "platforms;android-36" \ + "cmake;3.22.1" - name: Install dependencies run: npm ci --legacy-peer-deps - - name: Fix expo-modules-core shouldIncludeCompose bug + - name: Fix expo-modules-core Gradle bugs run: | - # The buildscript.ext properties are not accessible in the android {} block - # in newer Gradle versions. Re-declare at project level after the buildscript block. - sed -i "s|apply plugin: 'com.android.library'|// Fix: re-evaluate at project level so android {} block can access it\ndef _coreFeatures = project.findProperty(\"coreFeatures\") ?: []\next.shouldIncludeCompose = _coreFeatures.contains(\"compose\")\n\napply plugin: 'com.android.library'|" \ - node_modules/expo-modules-core/android/build.gradle - # Also change 'compose shouldIncludeCompose' to 'compose = shouldIncludeCompose' (property setter syntax) - sed -i 's/^ compose shouldIncludeCompose$/ compose = shouldIncludeCompose/' \ - node_modules/expo-modules-core/android/build.gradle + FILE="node_modules/expo-modules-core/android/build.gradle" + + # 1. Re-declare shouldIncludeCompose at project level (buildscript.ext not + # accessible in android {} block with newer Gradle/AGP versions) + sed -i "s|apply plugin: 'com.android.library'|// Fix: project-level re-declaration\ndef _coreFeatures = project.findProperty(\"coreFeatures\") ?: []\next.shouldIncludeCompose = _coreFeatures.contains(\"compose\")\n\napply plugin: 'com.android.library'|" \ + "$FILE" + + # 2. Use property assignment syntax instead of method call + # 'compose shouldIncludeCompose' -> 'compose = shouldIncludeCompose' + sed -i 's/^\s*compose shouldIncludeCompose\s*$/ compose = shouldIncludeCompose/' \ + "$FILE" + + echo "=== Patched lines around the fix ===" + grep -n "shouldIncludeCompose\|apply plugin" "$FILE" | head -20 - name: Build debug APK run: | cd android - export ANDROID_HOME=$ANDROID_SDK_ROOT - ./gradlew :app:assembleDebug --no-daemon --stacktrace 2>&1 + ./gradlew :app:assembleDebug \ + --no-daemon \ + --info \ + -Pandroid.overridePathCheck=true \ + 2>&1 | tail -200 env: - ANDROID_SDK_ROOT: ${{ env.ANDROID_SDK_ROOT }} - GRADLE_OPTS: -Xmx4g -XX:MaxMetaspaceSize=512m + ANDROID_HOME: ${{ env.ANDROID_SDK_ROOT }} + GRADLE_OPTS: "-Xmx4g -XX:MaxMetaspaceSize=512m" - name: Find APK - run: find android -name "*.apk" -type f + if: always() + run: find android -name "*.apk" -type f 2>/dev/null || echo "No APK found" - name: Upload APK artifact uses: actions/upload-artifact@v4 From 28ace4f35e90fbe2e7bc24fa964b79432836aa7f Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Apr 2026 16:49:48 +0000 Subject: [PATCH 06/14] fix: correct APK artifact upload path The build succeeded but upload failed due to path mismatch. Now finds the APK dynamically, copies to artifacts/ dir, then uploads. https://claude.ai/code/session_01MQS5o6eJUphagSBZKMQD1w --- .github/workflows/build-apk.yml | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-apk.yml b/.github/workflows/build-apk.yml index 13d5b39..2616cc4 100644 --- a/.github/workflows/build-apk.yml +++ b/.github/workflows/build-apk.yml @@ -69,13 +69,22 @@ jobs: ANDROID_HOME: ${{ env.ANDROID_SDK_ROOT }} GRADLE_OPTS: "-Xmx4g -XX:MaxMetaspaceSize=512m" - - name: Find APK - if: always() - run: find android -name "*.apk" -type f 2>/dev/null || echo "No APK found" + - name: Find and rename APK + run: | + APK=$(find android/app/build/outputs/apk -name "*.apk" -type f | head -1) + echo "Found APK: $APK" + if [ -n "$APK" ]; then + mkdir -p artifacts + cp "$APK" "artifacts/AeroStaffPro-debug.apk" + ls -lh artifacts/ + else + echo "ERROR: No APK found" + find android/app/build -type f | head -20 + exit 1 + fi - name: Upload APK artifact uses: actions/upload-artifact@v4 with: name: AeroStaffPro-debug - path: android/app/build/outputs/apk/debug/*.apk - if-no-files-found: error + path: artifacts/AeroStaffPro-debug.apk From 88a8890bd1191c593ef56d69ff88046fe205a397 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 9 Apr 2026 10:10:23 +0000 Subject: [PATCH 07/14] fix: salva turno manuale sempre visibile sopra la tastiera - KeyboardAvoidingView behavior='height' su Android (prima undefined = nessun effetto) - Form fields wrappati in ScrollView con keyboardShouldPersistTaps='handled' - Pulsante 'Salva Turno' spostato fuori dallo scroll per restare sempre visibile https://claude.ai/code/session_01MQS5o6eJUphagSBZKMQD1w --- src/screens/CalendarScreen.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/screens/CalendarScreen.tsx b/src/screens/CalendarScreen.tsx index 1266625..f44a8a6 100644 --- a/src/screens/CalendarScreen.tsx +++ b/src/screens/CalendarScreen.tsx @@ -495,7 +495,7 @@ export default function CalendarScreen() { setManualModalOpen(false)}> @@ -508,6 +508,8 @@ export default function CalendarScreen() { + {/* Form fields scrollable so the Save button stays visible above keyboard */} + {/* Data */} DATA )} + - + {/* Salva button outside scroll — sempre visibile sopra la tastiera */} + Salva Turno From 9153a96795379f391944898156c4eed8417b3db4 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 9 Apr 2026 10:18:03 +0000 Subject: [PATCH 08/14] Replace numpad TextInputs with inline scroll wheel pickers for shift time entry The KeyboardAvoidingView inside Modal doesn't work reliably on Android, causing the numpad keyboard to cover the "Salva Turno" button. Replace hour/minute TextInputs with FlatList-based carousel wheel pickers (no keyboard involved) so the save button is always accessible. https://claude.ai/code/session_01MQS5o6eJUphagSBZKMQD1w --- src/screens/CalendarScreen.tsx | 146 ++++++++++++++++----------------- 1 file changed, 70 insertions(+), 76 deletions(-) diff --git a/src/screens/CalendarScreen.tsx b/src/screens/CalendarScreen.tsx index f44a8a6..d240b33 100644 --- a/src/screens/CalendarScreen.tsx +++ b/src/screens/CalendarScreen.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect, useRef, useMemo } from 'react'; import { View, Text, StyleSheet, ActivityIndicator, ScrollView, TouchableOpacity, - PanResponder, Platform, UIManager, Animated, Dimensions, Modal, Alert, FlatList, TextInput, KeyboardAvoidingView, Keyboard, + PanResponder, Platform, UIManager, Animated, Dimensions, Modal, Alert, FlatList, TextInput, } from 'react-native'; import * as SystemCalendar from 'expo-calendar'; import * as Location from 'expo-location'; @@ -26,6 +26,58 @@ import { const PRIMARY = '#2563EB'; const STORAGE_KEY = '@shift_import_name'; +// ─── Wheel picker arrays ────────────────────────────────────────────────────── +const HOURS = Array.from({ length: 24 }, (_, i) => String(i).padStart(2, '0')); +const MINUTES = Array.from({ length: 60 }, (_, i) => String(i).padStart(2, '0')); +const WHEEL_ITEM_H = 48; + +function WheelPicker({ items, value, onChange, textColor }: { + items: string[]; + value: string; + onChange: (v: string) => void; + textColor: string; +}) { + const listRef = useRef(null); + const idx = items.indexOf(value); + + useEffect(() => { + if (idx >= 0) { + setTimeout(() => { + listRef.current?.scrollToOffset({ offset: idx * WHEEL_ITEM_H, animated: false }); + }, 80); + } + }, []); + + return ( + + item} + snapToInterval={WHEEL_ITEM_H} + decelerationRate="fast" + showsVerticalScrollIndicator={false} + contentContainerStyle={{ paddingVertical: WHEEL_ITEM_H }} + getItemLayout={(_, i) => ({ length: WHEEL_ITEM_H, offset: WHEEL_ITEM_H * i, index: i })} + onMomentumScrollEnd={e => { + const i = Math.round(e.nativeEvent.contentOffset.y / WHEEL_ITEM_H); + const clamped = Math.max(0, Math.min(items.length - 1, i)); + onChange(items[clamped]); + }} + renderItem={({ item }) => ( + + {item} + + )} + /> + + + ); +} + type ShiftEvent = { id: string; title: string; @@ -84,10 +136,6 @@ export default function CalendarScreen() { const [manualStartM, setManualStartM] = useState('00'); const [manualEndH, setManualEndH] = useState('16'); const [manualEndM, setManualEndM] = useState('00'); - const manualStartMRef = useRef(null); - const manualEndHRef = useRef(null); - const manualEndMRef = useRef(null); - const openManualEntry = () => { setEditMenuOpen(false); setManualDate(selectedDay); @@ -97,8 +145,6 @@ export default function CalendarScreen() { setManualModalOpen(true); }; - const sanitizeTimePart = (value: string) => value.replace(/\D/g, '').slice(0, 2); - const saveManualShift = async () => { const { status } = await SystemCalendar.requestCalendarPermissionsAsync(); if (status !== 'granted') { Alert.alert('Permesso negato'); return; } @@ -493,12 +539,8 @@ export default function CalendarScreen() { {/* ─── Manual Entry Modal ─── */} setManualModalOpen(false)}> - - + + setManualModalOpen(false)} /> @@ -508,8 +550,6 @@ export default function CalendarScreen() { - {/* Form fields scrollable so the Save button stays visible above keyboard */} - {/* Data */} DATA - {/* Orari (solo lavoro) */} + {/* Orari (solo lavoro) — carosello scroll, nessuna tastiera */} {manualType === 'Lavoro' && ( <> ORARIO INIZIO - - setManualStartH(sanitizeTimePart(v))} - selectTextOnFocus - returnKeyType="next" - blurOnSubmit={false} - onSubmitEditing={() => manualStartMRef.current?.focus()} - /> - setManualStartM(sanitizeTimePart(v))} - selectTextOnFocus - returnKeyType="next" - blurOnSubmit={false} - onSubmitEditing={() => manualEndHRef.current?.focus()} - /> + + + + : + + ORARIO FINE - - setManualEndH(sanitizeTimePart(v))} - selectTextOnFocus - returnKeyType="next" - blurOnSubmit={false} - onSubmitEditing={() => manualEndMRef.current?.focus()} - /> - setManualEndM(sanitizeTimePart(v))} - selectTextOnFocus - returnKeyType="done" - onSubmitEditing={Keyboard.dismiss} - /> + + + + : + + )} - - {/* Salva button outside scroll — sempre visibile sopra la tastiera */} - + Salva Turno - + {/* Hidden WebView for PDF extraction */} From f2d9c4e63b10e20b22a62b22d30f970de63eaa4e Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 9 Apr 2026 16:24:31 +0000 Subject: [PATCH 09/14] Speed up pull-to-refresh in FlightScreen Three improvements: - Add 2-min in-memory cache for FR24 flight data; subsequent refreshes within the window skip the network call entirely - Run calendar fetch and pinned-flight check in parallel (Promise.all) instead of sequentially after the network call - Move widget cache update and notification scheduling to fire-and-forget so the refresh spinner stops as soon as UI data is ready https://claude.ai/code/session_01MQS5o6eJUphagSBZKMQD1w --- src/screens/FlightScreen.tsx | 232 +++++++++++++++++++---------------- 1 file changed, 127 insertions(+), 105 deletions(-) diff --git a/src/screens/FlightScreen.tsx b/src/screens/FlightScreen.tsx index 155fb94..81f5d63 100644 --- a/src/screens/FlightScreen.tsx +++ b/src/screens/FlightScreen.tsx @@ -11,7 +11,7 @@ import { MaterialIcons } from '@expo/vector-icons'; import { useAppTheme } from '../context/ThemeContext'; import { useAirport } from '../context/AirportContext'; import { getAirlineOps, getAirlineColor } from '../utils/airlineOps'; -import { fetchAirportScheduleRaw } from '../utils/fr24api'; +import { fetchAirportScheduleRaw, type FR24ScheduleRaw } from '../utils/fr24api'; import { formatAirportHeader } from '../utils/airportSettings'; import { requestWidgetUpdate } from 'react-native-android-widget'; import { WIDGET_CACHE_KEY } from '../widgets/widgetTaskHandler'; @@ -25,6 +25,10 @@ const NOTIF_ENABLED_KEY = 'aerostaff_notif_enabled'; const PINNED_FLIGHT_KEY = 'pinned_flight_v1'; const PINNED_NOTIF_IDS_KEY = 'pinned_notif_ids_v1'; +// ─── In-memory flight cache ──────────────────────────────────────────────────── +let _flightCache: { data: FR24ScheduleRaw; ts: number } | null = null; +const CACHE_TTL_MS = 2 * 60 * 1000; // 2 minuti + // Handler: mostra notifiche anche con app aperta (wrapped for Expo Go compat) try { Notifications.setNotificationHandler({ handleNotification: async () => ({ @@ -255,13 +259,19 @@ export default function FlightScreen() { if (airportLoading) return; try { - const { - allArrivals, - departures: fetchedDepartures, - arrivals: fetchedArrivals, - } = await fetchAirportScheduleRaw(airportCode); + // 1. Voli: usa cache in-memory se fresca (< 2 min), altrimenti fetch + const now = Date.now(); + let rawResult: FR24ScheduleRaw; + if (_flightCache && (now - _flightCache.ts) < CACHE_TTL_MS) { + rawResult = _flightCache.data; + } else { + rawResult = await fetchAirportScheduleRaw(airportCode); + _flightCache = { data: rawResult, ts: now }; + } - // Build inbound arrival map: registration → best known arrival timestamp + const { allArrivals, departures: fetchedDepartures, arrivals: fetchedArrivals } = rawResult; + + // Build inbound arrival map const inboundMap: Record = {}; for (const a of allArrivals) { const reg = a.flight?.aircraft?.registration; @@ -272,12 +282,46 @@ export default function FlightScreen() { if (t) inboundMap[reg] = t; } setInboundArrivals(inboundMap); - setArrivals(fetchedArrivals); setDepartures(fetchedDepartures); - // Auto-clear expired pinned flight or stale data from another airport - const pinnedRaw = await AsyncStorage.getItem(PINNED_FLIGHT_KEY); + // 2. Pinned check + calendario IN PARALLELO + const [pinnedRaw, calResult] = await Promise.all([ + AsyncStorage.getItem(PINNED_FLIGHT_KEY), + (async () => { + let shiftToday: { start: number; end: number } | null = null; + let shiftTomorrow: { start: number; end: number } | null = null; + let isRestDay = false; + const { status } = await Calendar.requestCalendarPermissionsAsync(); + if (status === 'granted') { + const cals = await Calendar.getCalendarsAsync(Calendar.EntityTypes.EVENT); + const cal = cals.find(c => c.allowsModifications && c.isPrimary) || cals.find(c => c.allowsModifications); + if (cal) { + const todayStart = new Date(); todayStart.setHours(0, 0, 0, 0); + const tomorrowEnd = new Date(todayStart); tomorrowEnd.setDate(tomorrowEnd.getDate() + 1); tomorrowEnd.setHours(23, 59, 59, 999); + const evts = await Calendar.getEventsAsync([cal.id], todayStart, tomorrowEnd); + const todayEnd = new Date(todayStart); todayEnd.setHours(23, 59, 59, 999); + const tomorrowStart = new Date(todayStart); tomorrowStart.setDate(tomorrowStart.getDate() + 1); + for (const e of evts) { + if (e.title.includes('Riposo')) { + const evtDay = new Date(e.startDate); + if (evtDay >= todayStart && evtDay <= todayEnd) isRestDay = true; + continue; + } + if (!e.title.includes('Lavoro')) continue; + const s = new Date(e.startDate).getTime() / 1000; + const en = new Date(e.endDate).getTime() / 1000; + const evtDay = new Date(e.startDate); + if (evtDay >= todayStart && evtDay <= todayEnd) shiftToday = { start: s, end: en }; + else if (evtDay >= tomorrowStart && evtDay <= tomorrowEnd) shiftTomorrow = { start: s, end: en }; + } + } + } + return { shiftToday, shiftTomorrow, isRestDay }; + })(), + ]); + + // 3. Processa pinned if (pinnedRaw) { try { const pinned = JSON.parse(pinnedRaw); @@ -296,106 +340,84 @@ export default function FlightScreen() { } catch {} } - // Shift (today + tomorrow) - let shiftToday: { start: number; end: number } | null = null; - let shiftTomorrow: { start: number; end: number } | null = null; - let isRestDay = false; - const { status } = await Calendar.requestCalendarPermissionsAsync(); - if (status === 'granted') { - const cals = await Calendar.getCalendarsAsync(Calendar.EntityTypes.EVENT); - const cal = cals.find(c => c.allowsModifications && c.isPrimary) || cals.find(c => c.allowsModifications); - if (cal) { - const todayStart = new Date(); todayStart.setHours(0, 0, 0, 0); - const tomorrowEnd = new Date(todayStart); tomorrowEnd.setDate(tomorrowEnd.getDate() + 1); tomorrowEnd.setHours(23, 59, 59, 999); - const evts = await Calendar.getEventsAsync([cal.id], todayStart, tomorrowEnd); - const todayEnd = new Date(todayStart); todayEnd.setHours(23, 59, 59, 999); - const tomorrowStart = new Date(todayStart); tomorrowStart.setDate(tomorrowStart.getDate() + 1); - for (const e of evts) { - if (e.title.includes('Riposo')) { - const evtDay = new Date(e.startDate); - if (evtDay >= todayStart && evtDay <= todayEnd) isRestDay = true; - continue; + // 4. Turni + const { shiftToday, shiftTomorrow, isRestDay } = calResult; + setShifts({ today: shiftToday, tomorrow: shiftTomorrow }); + + // 5. Widget cache (fire-and-forget) + ;(async () => { + try { + const fmtT = (ts: number) => new Date(ts * 1000).toLocaleTimeString('it-IT', { hour: '2-digit', minute: '2-digit' }); + const fmtOff = (dep: number, off: number) => fmtT(dep - off * 60); + const nowHH = fmtT(Date.now() / 1000); + let widgetData: WidgetData; + if (isRestDay) { + widgetData = { state: 'rest' }; + } else if (!shiftToday) { + widgetData = { state: 'no_shift' }; + } else { + const shiftLabel = `${fmtT(shiftToday.start)} – ${fmtT(shiftToday.end)}`; + const pinnedRawW = await AsyncStorage.getItem(PINNED_FLIGHT_KEY); + let pinnedFn: string | null = null; + if (pinnedRawW) { + try { pinnedFn = JSON.parse(pinnedRawW).flight?.identification?.number?.default || null; } catch {} } - if (!e.title.includes('Lavoro')) continue; - const s = new Date(e.startDate).getTime() / 1000; - const en = new Date(e.endDate).getTime() / 1000; - const evtDay = new Date(e.startDate); - if (evtDay >= todayStart && evtDay <= todayEnd) shiftToday = { start: s, end: en }; - else if (evtDay >= tomorrowStart && evtDay <= tomorrowEnd) shiftTomorrow = { start: s, end: en }; + const wFlights: WidgetFlight[] = fetchedDepartures + .filter(item => { + const ts = item.flight?.time?.scheduled?.departure; + if (ts == null) return false; + const airline = item.flight?.airline?.name || ''; + const ops = getAirlineOps(airline); + const ciO = ts - ops.checkInOpen * 60, ciC = ts - ops.checkInClose * 60; + const gO = ts - ops.gateOpen * 60, gC = ts - ops.gateClose * 60; + return (ciO <= shiftToday!.end && ciC >= shiftToday!.start) || (gO <= shiftToday!.end && gC >= shiftToday!.start); + }) + .map(item => { + const ts = item.flight.time.scheduled.departure; + const airline = item.flight?.airline?.name || 'Sconosciuta'; + const ops = getAirlineOps(airline); + const fn = item.flight?.identification?.number?.default || 'N/A'; + return { + flightNumber: fn, + destinationIata: item.flight?.airport?.destination?.code?.iata || '???', + departureTs: ts, + departureTime: fmtT(ts), + ciOpen: fmtOff(ts, ops.checkInOpen), ciClose: fmtOff(ts, ops.checkInClose), + gateOpen: fmtOff(ts, ops.gateOpen), gateClose: fmtOff(ts, ops.gateClose), + airlineColor: getAirlineColor(airline), + isPinned: fn === pinnedFn, + }; + }) + .sort((a, b) => a.departureTs - b.departureTs); + widgetData = wFlights.length === 0 + ? { state: 'work_empty', shiftLabel, updatedAt: nowHH } + : { state: 'work', shiftLabel, flights: wFlights, updatedAt: nowHH }; } - } - } - setShifts({ today: shiftToday, tomorrow: shiftTomorrow }); + await AsyncStorage.setItem(WIDGET_CACHE_KEY, JSON.stringify(widgetData)); + if (Platform.OS === 'android') { + requestWidgetUpdate({ widgetName: 'ShiftFlights', renderWidget: () => () as any }).catch(() => {}); + } + } catch {} + })(); - // ── Push data to widget cache ── - try { - const fmtT = (ts: number) => new Date(ts * 1000).toLocaleTimeString('it-IT', { hour: '2-digit', minute: '2-digit' }); - const fmtOff = (dep: number, off: number) => fmtT(dep - off * 60); - const nowHH = fmtT(Date.now() / 1000); - - let widgetData: WidgetData; - if (isRestDay) { - widgetData = { state: 'rest' }; - } else if (!shiftToday) { - widgetData = { state: 'no_shift' }; - } else { - const shiftLabel = `${fmtT(shiftToday.start)} – ${fmtT(shiftToday.end)}`; - const pinnedRawW = await AsyncStorage.getItem(PINNED_FLIGHT_KEY); - let pinnedFn: string | null = null; - if (pinnedRawW) { - try { pinnedFn = JSON.parse(pinnedRawW).flight?.identification?.number?.default || null; } catch {} + // 6. Notifiche fire-and-forget — non blocca lo spinner + ;(async () => { + try { + const enabled = (await AsyncStorage.getItem(NOTIF_ENABLED_KEY)) === 'true'; + if (enabled && shiftToday) { + const shiftFlights = fetchedArrivals.filter(item => { + const ts = item.flight?.time?.scheduled?.arrival; + return ts && ts >= shiftToday!.start && ts <= shiftToday!.end; + }); + const count = await scheduleShiftNotifications(shiftFlights, shiftToday!.end); + setScheduledCount(count); + } else { + await cancelPreviousNotifications(); + setScheduledCount(0); } - const wFlights: WidgetFlight[] = fetchedDepartures - .filter(item => { - const ts = item.flight?.time?.scheduled?.departure; - if (ts == null) return false; - const airline = item.flight?.airline?.name || ''; - const ops = getAirlineOps(airline); - const ciO = ts - ops.checkInOpen * 60, ciC = ts - ops.checkInClose * 60; - const gO = ts - ops.gateOpen * 60, gC = ts - ops.gateClose * 60; - return (ciO <= shiftToday!.end && ciC >= shiftToday!.start) || (gO <= shiftToday!.end && gC >= shiftToday!.start); - }) - .map(item => { - const ts = item.flight.time.scheduled.departure; - const airline = item.flight?.airline?.name || 'Sconosciuta'; - const ops = getAirlineOps(airline); - const fn = item.flight?.identification?.number?.default || 'N/A'; - return { - flightNumber: fn, - destinationIata: item.flight?.airport?.destination?.code?.iata || '???', - departureTs: ts, - departureTime: fmtT(ts), - ciOpen: fmtOff(ts, ops.checkInOpen), ciClose: fmtOff(ts, ops.checkInClose), - gateOpen: fmtOff(ts, ops.gateOpen), gateClose: fmtOff(ts, ops.gateClose), - airlineColor: getAirlineColor(airline), - isPinned: fn === pinnedFn, - }; - }) - .sort((a, b) => a.departureTs - b.departureTs); - - widgetData = wFlights.length === 0 - ? { state: 'work_empty', shiftLabel, updatedAt: nowHH } - : { state: 'work', shiftLabel, flights: wFlights, updatedAt: nowHH }; - } - await AsyncStorage.setItem(WIDGET_CACHE_KEY, JSON.stringify(widgetData)); - if (Platform.OS === 'android') { - requestWidgetUpdate({ widgetName: 'ShiftFlights', renderWidget: () => () as any }).catch(() => {}); - } - } catch {} + } catch {} + })(); - // Schedula notifiche se attive (solo turno di oggi) - const enabled = (await AsyncStorage.getItem(NOTIF_ENABLED_KEY)) === 'true'; - if (enabled && shiftToday) { - const shiftFlights = fetchedArrivals.filter(item => { - const ts = item.flight?.time?.scheduled?.arrival; - return ts && ts >= shiftToday!.start && ts <= shiftToday!.end; - }); - const count = await scheduleShiftNotifications(shiftFlights, shiftToday!.end); - setScheduledCount(count); - } else { - await cancelPreviousNotifications(); - setScheduledCount(0); - } } catch (e) { console.error('[fetchAll]', e); } finally { setLoading(false); setRefreshing(false); } }, [airportCode, airportLoading]); From d4f0793256564186e3c9e05ddb645f5638003db0 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 9 Apr 2026 16:28:41 +0000 Subject: [PATCH 10/14] Fix APK update rejection: stable keystore + bump version to 2.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two root causes of "pacchetto non valido": 1. debug.keystore was missing from repo → each CI build generated a fresh key, incompatible with any previously installed APK. Fixed by generating and committing a stable debug.keystore. 2. versionCode was 1 (< installed 1.3.9) → Android rejects downgrades. Fixed by bumping to versionCode=200 / versionName="2.0.0". Note: existing 1.3.9 installs must be uninstalled once (different key); all future CI APKs will use this same keystore and upgrade cleanly. https://claude.ai/code/session_01MQS5o6eJUphagSBZKMQD1w --- .gitignore | 1 + android/app/build.gradle | 4 ++-- android/app/debug.keystore | Bin 0 -> 2666 bytes app.json | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 android/app/debug.keystore diff --git a/.gitignore b/.gitignore index 358f005..49bced3 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ expo-env.d.ts *.orig.* *.jks *.keystore +!android/app/debug.keystore *.p8 *.p12 *.key diff --git a/android/app/build.gradle b/android/app/build.gradle index 0b49f04..04a028e 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -92,8 +92,8 @@ android { applicationId 'com.anonymous.FlightWorkApp' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 1 - versionName "1.1.0" + versionCode 200 + versionName "2.0.0" buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\"" } diff --git a/android/app/debug.keystore b/android/app/debug.keystore new file mode 100644 index 0000000000000000000000000000000000000000..521559a31849dd69c5656a60c85b6107e443235d GIT binary patch literal 2666 zcma)8X*d*&7M>Y1!`O+jFQM$2u?<4@geX)75gJ>h&2GflC407!C0mvx`w~WtCHpX8 zvNc&ome7sDH@WU}@6-3(U-$kv&wI}Mp7;EGpM$_LyMurX2plsf6ebsI8vBzC$OtUL zF$2Lk7#)Fw9UyQJ!hcDSH((s()d^jBQe{w<|F~G0fuJHBMDK*uM!5fRK-dsygvQ?^ zD?%R3BgA$7YKHu4;L=>xJ&?mdv6HBCJ_sbu2?B~ESfNb+z6gRr00{hHP5W@fl z%7HoWW;&6^q+n&fNd0^!=1^xa4m^&2_lw@cSqQ=Cx-SS=`Af+Cj?bR0f0`%$MI1Id z$rp#*v|e6rZdHkfi)(mFI$m_h0AxaWu9?HaxTq;xa1iu_3Z0~ac12Ce&`TqK3Mw_qi3D=N$+W3id+ z<7h8-)n|Kf-dZLZJ)JAZ5Y44@7B zBc3Jmm2StNKZ@3;-dI|6X~l_X{FlJC1YoB*<607Ei+ka*h-9CytBprJ*!94&OF6V| zv}MUSi<4~lsIK!vLFH1(W{BMoVKY0u_)P4`b7_GD$?BWOB9o;8v7aY=9Z&r2y z@Jg(iouCX&>3Sl&O@%@40@0kmqz9u??3~4tr2y<lh)+j9J`>`PT?>**?u z`70Mrj8)vUlkwWGitS>$5TI`+tGMgUuEW7Qmi^2Nt8hA1RkLN_tQ-SODK{q-X*eLn zb6LGA27(zYReU09b4DZ@52gqlBK!V2tbn&0(%bZ<4%0LiRV)%ni62%%YVoG0&pT@? zwTh8Rew9$~Nb45epwG$BrD&@#6Kj^?68;Um$48xSK_;5?sBG$r`^Ul*ba?CS&rHHQ zjV+AgwQqJWQlX6=T563F36pI*#i>8<<1I6q?;LTx(Re-D7g}%1cmW%+(^Ji6;TS(U zM3?Q)S5VsW41};~&f?gAhgHXH$8KCI(k74803 zFpn2c{o)AB5pdh03*LmczRC2yTg{}jd@z?0Wt>=^6vPkX5$Ptp*s~`jyO8RSlX$;X zFma3N=tyfFmrv>pa&C86UFtphZd>q0pdrDR`cM(qD}(R9to_7cbv_kd{I)OrxDZ>$ zF;Y_ot+hEK+J*Ff%?;XyY;gUOr;DM_`{mw+%qP%n69CU@>T9?|GR-VI#a#JR*|rwd ziF<+u_F+vcE#i7;`4Fib|6b=s@_@N* zy?im^NEAv9rKXI~Kq_mfB5+{jpCv|U5e_VILWzQafD_>RTLJ#B;Fe%G>*}&EePik% z{Z?y$AkzcEWr_bMxN~_ONl|Z8C}`Ri>yFAPS_F=vhXQc2@0FTwwQN*JtAB_zi+mZ(I7N;pNG- zIJu4Z$M$tGusg2yDiNi6to+6e&9!_aV!-`rEb0d1cbb@SZRqUC`KNZl!jpNQo4(}y z+_>H(e1Q^ock=Lg_4Mw@+WX2J6v{B3ub3QQEvQh{f?m{3skPsQBkg*0o7MfW?O8#O zzxU!JQVW|q`&ap?yfgjjwN_@4d2XDs*XFRNU`7S2LY3CtZUtE_lJ7yjRUC?~5cKsq z+0N98*A<2X0|K^c9|+nom7GLL7{H;6mw}OfZmvGhqN{CK+eN5mCQnsEo*ddcd0e={ z`wO2^zGA5fh)aiunYAhTu0<56BkI;=0P_Mc35*Ihc%$XsSE$GPOk=DDJ0bbOqydAz0T> zHBlLs`s@&4>wskno?BI<>nf_T3BCU*OIbTu2LJg-ql2XX#2iPGu!q=Z+RN6&4ELnO z-^KCfNq*!33%%{71WC$GowFT~@v-cz@ zdta$H1a*9}NevU=U}eC%PgfTtnOOtO;h~oIm1nAdrKIAVXj+SzI=o`WQOhs4>gUJ% zVq&HlvZNbiWt$vfHiBdfgqp`YbiiUV6RneRG???#NNde)lKWbgh;_&@R?IGJ>p&!RK z70{=PGy*Hw+%yksB}08aZUqpVcZiMpJt`%8vQyy|vRZ=qNrwFm;9&DjHjE4^1*Jy| zQ5W~q!Co-#hHKC441BDcWw$UN7P0hJOV{&g;I3pwg)uu5AE{N$1T=S*O)b6O+Kx1- zw0)eJ)sp*ZF(1Oh>X|w+IqqqdJN&i*aT$R`u>JYJKtKopEItpL7Phfmy-&Q7F{nFU vZ!#X literal 0 HcmV?d00001 diff --git a/app.json b/app.json index 0473df0..94286f3 100644 --- a/app.json +++ b/app.json @@ -2,7 +2,7 @@ "expo": { "name": "FlightWorkApp", "slug": "FlightWorkApp", - "version": "1.1.0", + "version": "2.0.0", "orientation": "portrait", "icon": "./assets/icon.png", "userInterfaceStyle": "light", From c0a23aed804a46f7b68549fc223845745fe44857 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 9 Apr 2026 16:36:10 +0000 Subject: [PATCH 11/14] Add persistent shift notification pinned in notification shade MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When notifications are enabled and a shift is in progress, a sticky non-dismissible notification appears at the top showing: - Title: "✈️ Turno in corso: HH:MM – HH:MM" - Body: next incoming flight number and time (or "no arrivals") Implementation: - Dedicated low-importance Android channel "turno_attivo" (no sound/vibration) - sticky=true + ongoing=true so the user cannot swipe it away - Shown/updated on each fetchAll refresh while shift is active - Cleared when notifications are disabled or shift is outside hours - Channel setup on app mount via setupShiftNotifChannel() https://claude.ai/code/session_01MQS5o6eJUphagSBZKMQD1w --- src/screens/FlightScreen.tsx | 96 ++++++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 4 deletions(-) diff --git a/src/screens/FlightScreen.tsx b/src/screens/FlightScreen.tsx index 81f5d63..02b4356 100644 --- a/src/screens/FlightScreen.tsx +++ b/src/screens/FlightScreen.tsx @@ -24,6 +24,8 @@ const NOTIF_IDS_KEY = 'aerostaff_notif_ids_v1'; const NOTIF_ENABLED_KEY = 'aerostaff_notif_enabled'; const PINNED_FLIGHT_KEY = 'pinned_flight_v1'; const PINNED_NOTIF_IDS_KEY = 'pinned_notif_ids_v1'; +const SHIFT_NOTIF_ID_KEY = 'aerostaff_shift_notif_id_v1'; +const SHIFT_NOTIF_CHANNEL = 'turno_attivo'; // ─── In-memory flight cache ──────────────────────────────────────────────────── let _flightCache: { data: FR24ScheduleRaw; ts: number } | null = null; @@ -233,6 +235,59 @@ async function schedulePinnedNotifications(item: any, tab: 'arrivals' | 'departu } } +// ─── Notifica persistente turno ─────────────────────────────────────────────── +async function setupShiftNotifChannel() { + if (Platform.OS !== 'android') return; + try { + await Notifications.setNotificationChannelAsync(SHIFT_NOTIF_CHANNEL, { + name: 'Turno in corso', + importance: Notifications.AndroidImportance.LOW, // no pop-up, no suono + lockscreenVisibility: Notifications.AndroidNotificationVisibility.PUBLIC, + sound: null, + vibrationPattern: [0], + enableLights: false, + showBadge: false, + }); + } catch {} +} + +async function cancelShiftNotification() { + const id = await AsyncStorage.getItem(SHIFT_NOTIF_ID_KEY); + if (!id) return; + await Promise.all([ + Notifications.dismissNotificationAsync(id).catch(() => {}), + Notifications.cancelScheduledNotificationAsync(id).catch(() => {}), + ]); + await AsyncStorage.removeItem(SHIFT_NOTIF_ID_KEY); +} + +async function showShiftNotification( + shift: { start: number; end: number }, + nextFlightLabel: string | null, +) { + await cancelShiftNotification(); + const fmtT = (ts: number) => + new Date(ts * 1000).toLocaleTimeString('it-IT', { hour: '2-digit', minute: '2-digit' }); + + const id = await Notifications.scheduleNotificationAsync({ + content: { + title: `✈️ Turno in corso: ${fmtT(shift.start)} – ${fmtT(shift.end)}`, + body: nextFlightLabel ?? 'Nessun volo imminente nel turno', + sticky: true, + sound: false, + data: { type: 'shift_active' }, + android: { + channelId: SHIFT_NOTIF_CHANNEL, + ongoing: true, + color: '#2563EB', + smallIcon: 'ic_notification', + } as any, + }, + trigger: null, + }); + await AsyncStorage.setItem(SHIFT_NOTIF_ID_KEY, id); +} + // ─── Screen ──────────────────────────────────────────────────────────────────── export default function FlightScreen() { const { colors } = useAppTheme(); @@ -404,15 +459,30 @@ export default function FlightScreen() { ;(async () => { try { const enabled = (await AsyncStorage.getItem(NOTIF_ENABLED_KEY)) === 'true'; - if (enabled && shiftToday) { + const now = Date.now() / 1000; + if (enabled && shiftToday && now >= shiftToday.start && now <= shiftToday.end) { + // Notifiche temporizzate (arrivi + fine turno) const shiftFlights = fetchedArrivals.filter(item => { const ts = item.flight?.time?.scheduled?.arrival; return ts && ts >= shiftToday!.start && ts <= shiftToday!.end; }); const count = await scheduleShiftNotifications(shiftFlights, shiftToday!.end); setScheduledCount(count); + // Notifica persistente: prossimo volo in arrivo nel turno + const nextArrival = fetchedArrivals + .filter(item => { + const ts = item.flight?.time?.scheduled?.arrival; + return ts && ts > now && ts >= shiftToday!.start && ts <= shiftToday!.end; + }) + .sort((a, b) => (a.flight?.time?.scheduled?.arrival ?? 0) - (b.flight?.time?.scheduled?.arrival ?? 0))[0]; + const fmtT2 = (ts: number) => new Date(ts * 1000).toLocaleTimeString('it-IT', { hour: '2-digit', minute: '2-digit' }); + const nextLabel = nextArrival + ? `Prossimo arrivo: ${nextArrival.flight?.identification?.number?.default ?? 'N/A'} alle ${fmtT2(nextArrival.flight.time.scheduled.arrival)}` + : 'Nessun arrivo imminente'; + await showShiftNotification(shiftToday!, nextLabel); } else { await cancelPreviousNotifications(); + await cancelShiftNotification(); setScheduledCount(0); } } catch {} @@ -436,6 +506,8 @@ export default function FlightScreen() { if (id) setPinnedFlightId(id); } catch {} }); + // Crea il canale notifiche persistenti all'avvio + setupShiftNotifChannel(); }, []); // Toggle notifiche @@ -451,24 +523,40 @@ export default function FlightScreen() { if (!next) { await cancelPreviousNotifications(); + await cancelShiftNotification(); setScheduledCount(0); return; } // Schedula subito con i dati già caricati (turno di oggi) - if (shifts.today) { + const now = Date.now() / 1000; + if (shifts.today && now >= shifts.today.start && now <= shifts.today.end) { const shiftFlights = arrivals.filter(item => { const ts = item.flight?.time?.scheduled?.arrival; return ts && ts >= shifts.today!.start && ts <= shifts.today!.end; }); const count = await scheduleShiftNotifications(shiftFlights, shifts.today!.end); setScheduledCount(count); + // Mostra notifica persistente + const nextArrival = arrivals + .filter(item => { + const ts = item.flight?.time?.scheduled?.arrival; + return ts && ts > now && ts >= shifts.today!.start && ts <= shifts.today!.end; + }) + .sort((a, b) => (a.flight?.time?.scheduled?.arrival ?? 0) - (b.flight?.time?.scheduled?.arrival ?? 0))[0]; + const fmtT = (ts: number) => new Date(ts * 1000).toLocaleTimeString('it-IT', { hour: '2-digit', minute: '2-digit' }); + const nextLabel = nextArrival + ? `Prossimo arrivo: ${nextArrival.flight?.identification?.number?.default ?? 'N/A'} alle ${fmtT(nextArrival.flight.time.scheduled.arrival)}` + : 'Nessun arrivo imminente'; + await showShiftNotification(shifts.today!, nextLabel); Alert.alert( 'Notifiche attivate', count > 0 - ? `Programmate ${count} notifiche: arrivi voli (15 min prima) + fine turno.` - : 'Nessun volo futuro trovato, ma riceverai la notifica di fine turno.', + ? `Programmate ${count} notifiche + notifica turno in alto.` + : 'Notifica turno attiva. Nessun volo futuro trovato.', ); + } else if (shifts.today) { + Alert.alert('Turno non in corso', 'Le notifiche si attiveranno automaticamente all\'inizio del turno.'); } else { Alert.alert('Nessun turno trovato', 'Non ho trovato un turno "Lavoro" per oggi nel calendario.'); setNotifsEnabled(false); From bf23e12949b5685d9b76c6298ee635628e0de993 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 9 Apr 2026 16:37:20 +0000 Subject: [PATCH 12/14] Auto-publish APK to GitHub Releases after every CI build - Read versionName/versionCode from build.gradle - Rename APK to AeroStaffPro-v.apk - Create/update release tag v via softprops/action-gh-release@v2 - Added permissions: contents: write to allow release creation https://claude.ai/code/session_01MQS5o6eJUphagSBZKMQD1w --- .github/workflows/build-apk.yml | 41 ++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-apk.yml b/.github/workflows/build-apk.yml index 2616cc4..8857e82 100644 --- a/.github/workflows/build-apk.yml +++ b/.github/workflows/build-apk.yml @@ -9,6 +9,8 @@ on: jobs: build: runs-on: ubuntu-latest + permissions: + contents: write # necessario per creare/aggiornare release steps: - name: Checkout @@ -69,13 +71,23 @@ jobs: ANDROID_HOME: ${{ env.ANDROID_SDK_ROOT }} GRADLE_OPTS: "-Xmx4g -XX:MaxMetaspaceSize=512m" + - name: Read version from build.gradle + id: version + run: | + VERSION_NAME=$(grep 'versionName' android/app/build.gradle | grep -oP '"\K[^"]+') + VERSION_CODE=$(grep 'versionCode' android/app/build.gradle | grep -oP '\d+') + echo "name=$VERSION_NAME" >> $GITHUB_OUTPUT + echo "code=$VERSION_CODE" >> $GITHUB_OUTPUT + echo "tag=v$VERSION_NAME" >> $GITHUB_OUTPUT + echo "Version: $VERSION_NAME (code $VERSION_CODE)" + - name: Find and rename APK run: | APK=$(find android/app/build/outputs/apk -name "*.apk" -type f | head -1) echo "Found APK: $APK" if [ -n "$APK" ]; then mkdir -p artifacts - cp "$APK" "artifacts/AeroStaffPro-debug.apk" + cp "$APK" "artifacts/AeroStaffPro-v${{ steps.version.outputs.name }}.apk" ls -lh artifacts/ else echo "ERROR: No APK found" @@ -86,5 +98,28 @@ jobs: - name: Upload APK artifact uses: actions/upload-artifact@v4 with: - name: AeroStaffPro-debug - path: artifacts/AeroStaffPro-debug.apk + name: AeroStaffPro-v${{ steps.version.outputs.name }} + path: artifacts/AeroStaffPro-v${{ steps.version.outputs.name }}.apk + + - name: Create or update GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ steps.version.outputs.tag }} + name: AeroStaff Pro ${{ steps.version.outputs.name }} + body: | + ## AeroStaff Pro ${{ steps.version.outputs.name }} + + Build automatico dal branch `claude/build-aerostaffpro-apk-R00yT`. + + ### Novità in questo build + - Carosello ore/minuti nella modifica turni manuali (no tastiera) + - Pull-to-refresh velocizzato (cache 2 min + parallelo) + - Notifica persistente turno in corso (sticky in area notifiche) + - Keystore stabile: aggiornamenti futuri senza disinstallazione + + ### Installazione + 1. Scarica `AeroStaffPro-v${{ steps.version.outputs.name }}.apk` + 2. Se hai già installato una versione con keystore diverso: disinstalla prima + 3. Abilita "Installa da fonti sconosciute" se richiesto + files: artifacts/AeroStaffPro-v${{ steps.version.outputs.name }}.apk + make_latest: true From f82e3d221870c2c2602ab461e701f8c9eab502bd Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 10 Apr 2026 06:14:19 +0000 Subject: [PATCH 13/14] Restore staffMonitor live gate/check-in/stand/belt numbers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New src/utils/staffMonitor.ts: fetches HTML from https://servizi.pisa-airport.com/staffMonitor/staffMonitor.html, auto-discovers column layout from table headers, handles ISO-8859-1, normalizes flight numbers (codeshares, leading-zero variants) - FlightScreen: polls staffMonitor every 60s, renders live pills under each flight card — CI/Gate/Stand for departures, Stand/Nastro for arrivals - Silent fallback when page unreachable (non-airport network) https://claude.ai/code/session_01MQS5o6eJUphagSBZKMQD1w --- src/screens/FlightScreen.tsx | 49 ++++++++- src/utils/staffMonitor.ts | 195 +++++++++++++++++++++++++++++++++++ 2 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 src/utils/staffMonitor.ts diff --git a/src/screens/FlightScreen.tsx b/src/screens/FlightScreen.tsx index 02b4356..a5ecd7d 100644 --- a/src/screens/FlightScreen.tsx +++ b/src/screens/FlightScreen.tsx @@ -17,6 +17,7 @@ import { requestWidgetUpdate } from 'react-native-android-widget'; import { WIDGET_CACHE_KEY } from '../widgets/widgetTaskHandler'; import type { WidgetData, WidgetFlight } from '../widgets/widgetTaskHandler'; import { ShiftWidget } from '../widgets/ShiftWidget'; +import { fetchStaffMonitor, lookupStaff, type StaffMonitorData } from '../utils/staffMonitor'; const WearDataSender = Platform.OS === 'android' ? NativeModules.WearDataSender : null; @@ -304,12 +305,27 @@ export default function FlightScreen() { const [scheduledCount, setScheduledCount] = useState(0); const [pinnedFlightId, setPinnedFlightId] = useState(null); const [inboundArrivals, setInboundArrivals] = useState>({}); + const [staffData, setStaffData] = useState(null); // Carica preferenza notifiche salvata useEffect(() => { AsyncStorage.getItem(NOTIF_ENABLED_KEY).then(v => setNotifsEnabled(v === 'true')); }, []); + // StaffMonitor: fetch immediato + polling 60s + useEffect(() => { + let cancelled = false; + const poll = async () => { + try { + const data = await fetchStaffMonitor(); + if (!cancelled) setStaffData(data); + } catch { /* rete aeroporto non raggiungibile — silenzioso */ } + }; + poll(); + const interval = setInterval(poll, 60_000); + return () => { cancelled = true; clearInterval(interval); }; + }, []); + const fetchAll = useCallback(async () => { if (airportLoading) return; @@ -762,11 +778,39 @@ export default function FlightScreen() { {statusText} )} + {/* ── StaffMonitor live pills ── */} + {(() => { + if (!staffData) return null; + const sf = lookupStaff(staffData, flightNumber, activeTab); + if (!sf) return null; + const pills: { icon: string; label: string; value: string }[] = []; + if (activeTab === 'departures') { + if (sf.checkIn) pills.push({ icon: 'desktop-windows', label: 'CI', value: sf.checkIn }); + if (sf.gate) pills.push({ icon: 'meeting-room', label: 'Gate', value: sf.gate }); + if (sf.stand) pills.push({ icon: 'local-parking', label: 'Stand', value: sf.stand }); + } else { + if (sf.stand) pills.push({ icon: 'local-parking', label: 'Stand', value: sf.stand }); + if (sf.belt) pills.push({ icon: 'luggage', label: 'Nastro', value: sf.belt }); + } + if (pills.length === 0) return null; + return ( + + {pills.map(p => ( + + + + {p.label}: {p.value} + + + ))} + + ); + })()} ); - }, [activeTab, userShift, s, pinnedFlightId, pinFlight, unpinFlight, inboundArrivals, colors]); + }, [activeTab, userShift, s, pinnedFlightId, pinFlight, unpinFlight, inboundArrivals, colors, staffData]); return ( @@ -860,6 +904,9 @@ function makeStyles(c: any) { pinBannerText: { color: '#fff', fontWeight: 'bold', fontSize: 11, letterSpacing: 0.5 }, statusPill: { paddingHorizontal: 8, paddingVertical: 3, borderRadius: 20, marginTop: 5 }, statusText: { fontSize: 10, fontWeight: '700' }, + staffRow: { flexDirection: 'row', flexWrap: 'wrap', gap: 6, marginTop: 8 }, + staffPill: { flexDirection: 'row', alignItems: 'center', gap: 4, backgroundColor: c.primaryLight, borderRadius: 8, paddingHorizontal: 8, paddingVertical: 4 }, + staffPillText: { fontSize: 11 }, cardHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 10, paddingHorizontal: 14 }, headerLeft: { flexDirection: 'row', alignItems: 'center', gap: 10 }, headerFlightNum: { color: '#fff', fontWeight: '900', fontSize: 15, lineHeight: 18 }, diff --git a/src/utils/staffMonitor.ts b/src/utils/staffMonitor.ts new file mode 100644 index 0000000..2ad0d38 --- /dev/null +++ b/src/utils/staffMonitor.ts @@ -0,0 +1,195 @@ +/** + * staffMonitor.ts + * Fetches live gate / check-in / stand / belt data from the Pisa airport + * staff monitoring page and exposes a lookup helper for FlightScreen. + */ + +const STAFF_MONITOR_URL = + 'https://servizi.pisa-airport.com/staffMonitor/staffMonitor.html'; +const FETCH_TIMEOUT_MS = 8000; + +// ─── Types ──────────────────────────────────────────────────────────────────── + +export type StaffFlight = { + gate?: string; + checkIn?: string; + stand?: string; + belt?: string; // nastro bagagli +}; + +export type StaffMonitorData = { + departures: Record; // normalized flightNum → data + arrivals: Record; + fetchedAt: number; +}; + +// ─── Helpers ────────────────────────────────────────────────────────────────── + +function stripHtml(s: string): string { + return s + .replace(/<[^>]+>/g, '') + .replace(/ /gi, ' ') + .replace(/&/gi, '&') + .replace(/</gi, '<') + .replace(/>/gi, '>') + .replace(/\s+/g, ' ') + .trim(); +} + +/** Normalize flight number: remove spaces, uppercase, strip leading zeros from digits */ +export function normalizeFlightNum(s: string): string { + // e.g. "FR 1234" → "FR1234", "W4 0234" → "W4234", "EJU1234" → "EJU1234" + return s + .replace(/\s+/g, '') + .toUpperCase() + .replace(/([A-Z]{1,3})0+(\d)/, '$1$2'); +} + +function findColIdx(headers: string[], ...keywords: string[]): number { + return headers.findIndex(h => + keywords.some(kw => h.toLowerCase().includes(kw.toLowerCase())), + ); +} + +// ─── Parser ─────────────────────────────────────────────────────────────────── + +function parseTables(html: string): StaffMonitorData { + const result: StaffMonitorData = { + departures: {}, + arrivals: {}, + fetchedAt: Date.now(), + }; + + // Match every …
+ const tableRe = //gi; + for (const tableMatch of html.matchAll(tableRe)) { + const tableHtml = tableMatch[0]; + + // All rows + const rows = [...tableHtml.matchAll(/]*>([\s\S]*?)<\/tr>/gi)].map( + m => m[1], + ); + if (rows.length < 2) continue; + + // Header row: accept both and + const headerCells = [ + ...rows[0].matchAll(/]*>([\s\S]*?)<\/t[hd]>/gi), + ].map(m => stripHtml(m[1]).toLowerCase()); + + // Must contain a "volo / flight / numero" column to be a flight table + const voloIdx = findColIdx( + headerCells, + 'volo', + 'flight', + 'numero', + 'n.volo', + 'n. volo', + ); + if (voloIdx === -1) continue; + + // Identify columns by keyword + const gateIdx = findColIdx(headerCells, 'gate'); + const checkInIdx = findColIdx(headerCells, 'check', 'ci', 'banco', 'accettazione', 'desk'); + const standIdx = findColIdx(headerCells, 'stand', 'piazzola', 'postaz', 'parcheggio'); + const beltIdx = findColIdx(headerCells, 'nastro', 'belt', 'bagagli', 'riconsegna', 'tappeto', 'carousel'); + + // Parse data rows (skip header) + for (const row of rows.slice(1)) { + const cells = [ + ...row.matchAll(/]*>([\s\S]*?)<\/td>/gi), + ].map(m => stripHtml(m[1])); + + if (cells.length <= voloIdx) continue; + const rawFlight = cells[voloIdx]; + if (!rawFlight) continue; + + const flightNum = normalizeFlightNum(rawFlight); + // Skip rows that don't look like a flight number (e.g. empty/header repeated) + if (!/^[A-Z0-9]{2,}\d+$/.test(flightNum)) continue; + + const data: StaffFlight = {}; + if (gateIdx !== -1 && cells[gateIdx]) data.gate = cells[gateIdx]; + if (checkInIdx !== -1 && cells[checkInIdx]) data.checkIn = cells[checkInIdx]; + if (standIdx !== -1 && cells[standIdx]) data.stand = cells[standIdx]; + if (beltIdx !== -1 && cells[beltIdx]) data.belt = cells[beltIdx]; + + // Classify into departures / arrivals based on which fields are populated. + // A row can appear in both if the table is mixed. + if (data.gate || data.checkIn) { + result.departures[flightNum] = { + ...result.departures[flightNum], + ...data, + }; + } + if (data.belt || (data.stand && !data.gate && !data.checkIn)) { + result.arrivals[flightNum] = { + ...result.arrivals[flightNum], + stand: data.stand, + belt: data.belt, + }; + } + } + } + + return result; +} + +// ─── Public API ─────────────────────────────────────────────────────────────── + +export async function fetchStaffMonitor(): Promise { + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS); + + try { + const res = await fetch(STAFF_MONITOR_URL, { + headers: { + 'User-Agent': + 'Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Mobile Safari/537.36', + Accept: 'text/html,application/xhtml+xml;q=0.9,*/*;q=0.8', + 'Accept-Language': 'it-IT,it;q=0.9,en;q=0.5', + }, + signal: controller.signal, + }); + + if (!res.ok) throw new Error(`HTTP ${res.status}`); + + const html = await res.text(); + return parseTables(html); + } finally { + clearTimeout(timer); + } +} + +/** + * Look up a FR24 flight number in the staffMonitor data. + * Tries several normalization strategies to handle codeshares and + * differing airline-code formats between FR24 and staffMonitor. + */ +export function lookupStaff( + data: StaffMonitorData, + flightNumber: string, + tab: 'departures' | 'arrivals', +): StaffFlight | null { + const pool = tab === 'departures' ? data.departures : data.arrivals; + if (!pool || Object.keys(pool).length === 0) return null; + + const normalized = normalizeFlightNum(flightNumber); + + // 1. Direct match + if (pool[normalized]) return pool[normalized]; + + // 2. Try without leading zeros in numeric part + const noZero = normalized.replace(/([A-Z]{1,3})0*(\d+)/, (_, p, n) => p + String(parseInt(n, 10))); + if (pool[noZero]) return pool[noZero]; + + // 3. Match on numeric suffix only (covers codeshare: FR1234 ↔ W41234) + const numSuffix = normalized.replace(/^[A-Z]+/, ''); + if (numSuffix.length >= 3) { + const found = Object.entries(pool).find(([k]) => + k.replace(/^[A-Z]+/, '') === numSuffix, + ); + if (found) return found[1]; + } + + return null; +} From 29adc6bc00101456c2a475ae83337e2632ad9936 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 10 Apr 2026 14:51:16 +0000 Subject: [PATCH 14/14] ci: trigger release build v2.0.0 https://claude.ai/code/session_01MQS5o6eJUphagSBZKMQD1w