From 5584fb5d52681684f3597a2d68fbc23827dc0080 Mon Sep 17 00:00:00 2001 From: Patrick Demers Date: Mon, 8 May 2023 23:13:44 -0500 Subject: [PATCH] integrate to use websocket on backend server --- app.config.js | 1 + assets/arrow.png | Bin 9789 -> 9470 bytes package-lock.json | 737 +++++++++++------- package.json | 4 +- src/Main.js | 4 +- src/components/Home.js | 48 +- src/components/Map/Map.js | 32 +- src/components/Map/components/ImagePin.js | 4 +- src/components/Map/components/RouteLine.js | 54 +- src/components/Map/components/Stops.js | 29 +- src/components/Map/components/VehicleView.js | 76 +- src/components/Map/components/Vehicles.js | 59 +- src/components/RouteSelect.js | 60 +- src/components/Settings/Rules/CreateRule.js | 179 +++++ .../StopInfo/components/StopDetailsView.js | 33 +- .../StopInfo/components/TopBanner.js | 31 +- .../StopInfo/components/UpcomingArrivals.js | 73 +- src/components/Websocket.js | 101 +++ src/state/atoms.js | 39 +- src/state/dispatcher.js | 34 +- src/state/dispatchers/data.js | 58 ++ src/state/dispatchers/loading.js | 7 + src/state/dispatchers/routes.js | 114 +-- src/state/dispatchers/stops.js | 19 + src/state/dispatchers/vehicles.js | 52 +- src/state/dispatchers/websocket.js | 7 + src/state/selectors.js | 216 +++-- 27 files changed, 1257 insertions(+), 814 deletions(-) create mode 100644 src/components/Settings/Rules/CreateRule.js create mode 100644 src/components/Websocket.js create mode 100644 src/state/dispatchers/data.js create mode 100644 src/state/dispatchers/loading.js create mode 100644 src/state/dispatchers/websocket.js diff --git a/app.config.js b/app.config.js index f2f3bc7..908c7ef 100644 --- a/app.config.js +++ b/app.config.js @@ -37,6 +37,7 @@ module.exports = { }, package: 'com.demerstech.amesride', }, + jsEngine: 'hermes', web: { favicon: './assets/favicon.png', }, diff --git a/assets/arrow.png b/assets/arrow.png index 74431c36a7c2e18a94989ed9fd4c8715107cc1aa..d006ca1345f9e3f98f5b9f3e4250c5aa253aaad6 100644 GIT binary patch literal 9470 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 diff --git a/package-lock.json b/package-lock.json index c40e13b..f68038c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,19 @@ { "name": "ames-ride", - "version": "1.1.0", + "version": "1.1.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ames-ride", - "version": "1.1.0", + "version": "1.1.1", "license": "GNU3", "dependencies": { "@aveq-research/localforage-asyncstorage-driver": "^3.0.1", "@react-native-async-storage/async-storage": "~1.17.3", "@react-navigation/native": "^6.0.13", "@react-navigation/native-stack": "^6.9.1", + "date-fns": "^2.29.3", "expo": "~47.0.8", "expo-clipboard": "~4.0.1", "expo-constants": "~14.0.2", @@ -35,7 +36,8 @@ "react-native-svg": "13.4.0", "react-native-swipe-gestures": "^1.0.5", "react-native-vector-icons": "^9.2.0", - "recoil": "^0.7.5" + "recoil": "^0.7.5", + "socket.io-client": "^4.5.4" }, "devDependencies": { "@babel/core": "^7.12.9", @@ -1910,15 +1912,15 @@ } }, "node_modules/@expo/cli": { - "version": "0.4.10", - "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-0.4.10.tgz", - "integrity": "sha512-c8NJOVa5b8g9CYj8ahdaN21cVE2wPwUaFrtTE0kLeRR5ASy8reWLFEOcstEtt6eufdcN/uGgBWQ0FLovgLZuzw==", + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-0.4.11.tgz", + "integrity": "sha512-L9Ci9RBh0aPFEDF1AjDYPk54OgeUJIKzxF3lRgITm+lQpI3IEKjAc9LaYeQeO1mlZMUQmPkHArF8iyz1eOeVoQ==", "dependencies": { "@babel/runtime": "^7.14.0", "@expo/code-signing-certificates": "0.0.5", "@expo/config": "~7.0.2", "@expo/config-plugins": "~5.0.3", - "@expo/dev-server": "0.1.123", + "@expo/dev-server": "0.1.124", "@expo/devcert": "^1.0.0", "@expo/json-file": "^8.2.35", "@expo/metro-config": "~0.5.0", @@ -2287,15 +2289,15 @@ } }, "node_modules/@expo/dev-server": { - "version": "0.1.123", - "resolved": "https://registry.npmjs.org/@expo/dev-server/-/dev-server-0.1.123.tgz", - "integrity": "sha512-N6UVzzeemfX0AONUSWInvkAAbqon8hRXpyYE/nMPaC6TvAmgGY5ILZAGoXwlpxwY2VKNT0Lx4s/UJ53ytIaHbA==", + "version": "0.1.124", + "resolved": "https://registry.npmjs.org/@expo/dev-server/-/dev-server-0.1.124.tgz", + "integrity": "sha512-iHczVcf+rgWupCY/3b3ePIizNtzsy1O/w8jdKv3bKvoOfXiVIVOo4KGiVDpAJOahKiMOsRlbKeemB8OLNKzdSA==", "dependencies": { "@expo/bunyan": "4.0.0", "@expo/metro-config": "~0.5.1", "@expo/osascript": "2.0.33", "@expo/spawn-async": "^1.5.0", - "body-parser": "1.19.0", + "body-parser": "^1.20.1", "chalk": "^4.0.0", "connect": "^3.7.0", "fs-extra": "9.0.0", @@ -2654,9 +2656,9 @@ } }, "node_modules/@expo/json-file/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dependencies": { "minimist": "^1.2.0" }, @@ -2756,11 +2758,11 @@ } }, "node_modules/@expo/package-manager": { - "version": "0.0.57", - "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-0.0.57.tgz", - "integrity": "sha512-Y4RpSL9EqaPF+Vd2GrK6r7Xx7Dv0Xdq3AGAD9C0KwV21WqP/scj/dpjxFY+ABwmdhNsFzYXb8fmDyh4tiKenPQ==", + "version": "0.0.60", + "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-0.0.60.tgz", + "integrity": "sha512-MdV7dpFA4aI8HIW0xsW2DBUem5aFKL8+/v5LXKPZuXmYW02/EXPSp7DBJAwow8ULpa3Q2VlYfb46hWPre3hw4A==", "dependencies": { - "@expo/json-file": "8.2.36", + "@expo/json-file": "^8.2.37", "@expo/spawn-async": "^1.5.0", "ansi-regex": "^5.0.0", "chalk": "^4.0.0", @@ -2772,6 +2774,24 @@ "sudo-prompt": "9.1.1" } }, + "node_modules/@expo/package-manager/node_modules/@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/@expo/package-manager/node_modules/@expo/json-file": { + "version": "8.2.37", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-8.2.37.tgz", + "integrity": "sha512-YaH6rVg11JoTS2P6LsW7ybS2CULjf40AbnAHw2F1eDPuheprNjARZMnyHFPkKv7GuxCy+B9GPcbOKgc4cgA80Q==", + "dependencies": { + "@babel/code-frame": "~7.10.4", + "json5": "^2.2.2", + "write-file-atomic": "^2.3.0" + } + }, "node_modules/@expo/package-manager/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -3128,11 +3148,11 @@ "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==" }, "node_modules/@graphql-typed-document-node/core": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.1.tgz", - "integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.2.tgz", + "integrity": "sha512-9anpBMM9mEgZN4wr2v8wHJI2/u5TnnggewRN6OlvXTTnuVyoY19X6rOv9XTqKRw6dcGKwZsBi8n0kDE2I5i4VA==", "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@hapi/hoek": { @@ -4581,9 +4601,9 @@ } }, "node_modules/@sideway/formula": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", - "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" }, "node_modules/@sideway/pinpoint": { "version": "2.0.0", @@ -4595,6 +4615,11 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, "node_modules/@types/geojson": { "version": "7946.0.10", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", @@ -5560,23 +5585,26 @@ "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==" }, "node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dependencies": { - "bytes": "3.1.0", - "content-type": "~1.0.4", + "bytes": "3.1.2", + "content-type": "~1.0.5", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, "node_modules/body-parser/node_modules/debug": { @@ -5592,6 +5620,17 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/body-parser/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -5724,9 +5763,9 @@ "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==" }, "node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "engines": { "node": ">= 0.8" } @@ -5807,7 +5846,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, "dependencies": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -6337,9 +6375,9 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "engines": { "node": ">= 0.6" } @@ -6517,6 +6555,18 @@ "resolved": "https://registry.npmjs.org/dag-map/-/dag-map-1.0.2.tgz", "integrity": "sha512-+LSAiGFwQ9dRnRdOeaj7g47ZFJcOUPukAP8J3A3fuZ1g9Y44BG+P1sgApjLXTQPOzC4+7S9Wr8kXsfpINM4jpw==" }, + "node_modules/date-fns": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", + "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==", + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/dayjs": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.6.tgz", @@ -6547,9 +6597,9 @@ } }, "node_modules/decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", "engines": { "node": ">=0.10" } @@ -6692,11 +6742,11 @@ "integrity": "sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg==" }, "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/deprecated-react-native-prop-types": { @@ -6830,6 +6880,46 @@ "once": "^1.4.0" } }, + "node_modules/engine.io-client": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.3.tgz", + "integrity": "sha512-aXPtgF1JS3RuuKcpSrBtimSjYvrbhKW9froICH4s0F3XQWLxsKNxqzG39nnvQZQnva4CMvUK63T7shevxRyYHw==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", + "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/entities": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", @@ -7792,25 +7882,25 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/expo": { - "version": "47.0.8", - "resolved": "https://registry.npmjs.org/expo/-/expo-47.0.8.tgz", - "integrity": "sha512-PGNCIvrnYwHH4TDFsVocq/xhWZ5DW8N3bLkZJPZZgX6VgjtVLNsbZ+0lm1inLCZHP+6xSpSKRccjGHO/QQoMBQ==", + "version": "47.0.13", + "resolved": "https://registry.npmjs.org/expo/-/expo-47.0.13.tgz", + "integrity": "sha512-9VjjGdViCJ9NfWbUE7brkwFBDvKuA35V345vMtHFYNKoGJjXib36yitmawreMDQFv0kMTqTnzc7T2191Pod7Ng==", "dependencies": { "@babel/runtime": "^7.14.0", - "@expo/cli": "0.4.10", + "@expo/cli": "0.4.11", "@expo/config": "7.0.3", "@expo/config-plugins": "5.0.4", "@expo/vector-icons": "^13.0.0", "babel-preset-expo": "~9.2.2", "cross-spawn": "^6.0.5", "expo-application": "~5.0.1", - "expo-asset": "~8.6.2", + "expo-asset": "~8.7.0", "expo-constants": "~14.0.2", "expo-file-system": "~15.1.1", "expo-font": "~11.0.1", "expo-keep-awake": "~11.0.1", - "expo-modules-autolinking": "1.0.0", - "expo-modules-core": "1.0.3", + "expo-modules-autolinking": "1.0.2", + "expo-modules-core": "1.1.1", "fbemitter": "^3.0.0", "getenv": "^1.0.0", "invariant": "^2.2.4", @@ -7835,9 +7925,9 @@ } }, "node_modules/expo-asset": { - "version": "8.6.2", - "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-8.6.2.tgz", - "integrity": "sha512-XqlXjkuUCEiojbHwbHPjQs1oboRz6w3eV96+9NBD+wb3EUqgAAYY2Do+IWyVCAl8UIFbFi3xzMiqk0Xm9+H8uQ==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-8.7.0.tgz", + "integrity": "sha512-lkoNsHK6vf+outISB6/37SonXcAL6Buw0ycjiwQVFfpOBKpkQa+zw5wm1m3KwjH2txmR3xdIzcpWsJkgovYCvQ==", "dependencies": { "blueimp-md5": "^2.10.0", "expo-constants": "~14.0.0", @@ -8010,9 +8100,9 @@ } }, "node_modules/expo-modules-autolinking": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-1.0.0.tgz", - "integrity": "sha512-MoRRkOVMoGUH/Lr8XS6UmBIZT/qrwbRt2IzUBALcM6MWZKtDn9Uct9XgMRxue82FJhRCfy9p1xZJVKHBRo4zEA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-1.0.2.tgz", + "integrity": "sha512-skAUXERKw1gtSw8xsvft9DE0KVhBvw4dujAtgCZoG2l513fN7ds+B5+30ZVgZATMC+EjtlmjKXzhp5QS44DCFA==", "dependencies": { "chalk": "^4.1.0", "commander": "^7.2.0", @@ -8122,9 +8212,9 @@ } }, "node_modules/expo-modules-core": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-1.0.3.tgz", - "integrity": "sha512-XqyA5c+zsK+cHDNVBVYu62HLBHyGMG0iWpXVP0bBQJWz0eyg5rcuEqLsnRTmoEz0YnH6QBf/cwRl+FfgnnH5Og==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-1.1.1.tgz", + "integrity": "sha512-+AcaYmaWphIfkBcccu65dyOhWnpOJ3+SQpoI4lI/Plg1nNjOLuBjmrdVvpiJOvkN+CqbNGsJ5Yll8LLk+C107Q==", "dependencies": { "compare-versions": "^3.4.0", "invariant": "^2.2.4" @@ -8717,7 +8807,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "dev": true, "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -8918,7 +9007,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -9043,24 +9131,27 @@ } }, "node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, - "node_modules/http-errors/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } }, "node_modules/https-proxy-agent": { "version": "5.0.1", @@ -10295,9 +10386,9 @@ "dev": true }, "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "bin": { "json5": "lib/cli.js" }, @@ -12002,7 +12093,6 @@ "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -12159,9 +12249,9 @@ } }, "node_modules/open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", @@ -12892,11 +12982,17 @@ } }, "node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, "engines": { "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/query-string": { @@ -12949,12 +13045,12 @@ } }, "node_modules/raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -13799,29 +13895,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/send/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/send/node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -13849,11 +13922,6 @@ "node": ">= 0.8" } }, - "node_modules/send/node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, "node_modules/send/node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -13862,14 +13930,6 @@ "node": ">= 0.8" } }, - "node_modules/send/node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, "node_modules/serialize-error": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-6.0.0.tgz", @@ -13953,9 +14013,9 @@ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" }, "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "node_modules/shallow-clone": { "version": "3.0.1", @@ -14001,7 +14061,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, "dependencies": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -14279,6 +14338,32 @@ "node": ">=0.10.0" } }, + "node_modules/socket.io-client": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.4.tgz", + "integrity": "sha512-ZpKteoA06RzkD32IbqILZ+Cnst4xewU7ZYK12aS1mzHftFFjpoMz69IuhP/nL25pJfao/amoPI527KnuhFm01g==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.2.3", + "socket.io-parser": "~4.2.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", + "integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", @@ -14757,13 +14842,13 @@ } }, "node_modules/tar": { - "version": "6.1.12", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.12.tgz", - "integrity": "sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw==", + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", + "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", + "minipass": "^4.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" @@ -14772,6 +14857,14 @@ "node": ">=10" } }, + "node_modules/tar/node_modules/minipass": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.4.tgz", + "integrity": "sha512-lwycX3cBMTvcejsHITUgYj6Gy6A7Nh4Q6h9NP4sTHY1ccJlC7yKzDmiShEHsJ16Jf1nKGDEaiHxiltsJEvk0nQ==", + "engines": { + "node": ">=8" + } + }, "node_modules/tar/node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -14981,9 +15074,9 @@ } }, "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "engines": { "node": ">=0.6" } @@ -15019,9 +15112,9 @@ } }, "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "dependencies": { "minimist": "^1.2.0" @@ -15106,9 +15199,9 @@ } }, "node_modules/ua-parser-js": { - "version": "0.7.32", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.32.tgz", - "integrity": "sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw==", + "version": "0.7.33", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz", + "integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==", "funding": [ { "type": "opencollective", @@ -15679,6 +15772,14 @@ "node": ">=8.0" } }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -17083,15 +17184,15 @@ } }, "@expo/cli": { - "version": "0.4.10", - "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-0.4.10.tgz", - "integrity": "sha512-c8NJOVa5b8g9CYj8ahdaN21cVE2wPwUaFrtTE0kLeRR5ASy8reWLFEOcstEtt6eufdcN/uGgBWQ0FLovgLZuzw==", + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-0.4.11.tgz", + "integrity": "sha512-L9Ci9RBh0aPFEDF1AjDYPk54OgeUJIKzxF3lRgITm+lQpI3IEKjAc9LaYeQeO1mlZMUQmPkHArF8iyz1eOeVoQ==", "requires": { "@babel/runtime": "^7.14.0", "@expo/code-signing-certificates": "0.0.5", "@expo/config": "~7.0.2", "@expo/config-plugins": "~5.0.3", - "@expo/dev-server": "0.1.123", + "@expo/dev-server": "0.1.124", "@expo/devcert": "^1.0.0", "@expo/json-file": "^8.2.35", "@expo/metro-config": "~0.5.0", @@ -17387,15 +17488,15 @@ } }, "@expo/dev-server": { - "version": "0.1.123", - "resolved": "https://registry.npmjs.org/@expo/dev-server/-/dev-server-0.1.123.tgz", - "integrity": "sha512-N6UVzzeemfX0AONUSWInvkAAbqon8hRXpyYE/nMPaC6TvAmgGY5ILZAGoXwlpxwY2VKNT0Lx4s/UJ53ytIaHbA==", + "version": "0.1.124", + "resolved": "https://registry.npmjs.org/@expo/dev-server/-/dev-server-0.1.124.tgz", + "integrity": "sha512-iHczVcf+rgWupCY/3b3ePIizNtzsy1O/w8jdKv3bKvoOfXiVIVOo4KGiVDpAJOahKiMOsRlbKeemB8OLNKzdSA==", "requires": { "@expo/bunyan": "4.0.0", "@expo/metro-config": "~0.5.1", "@expo/osascript": "2.0.33", "@expo/spawn-async": "^1.5.0", - "body-parser": "1.19.0", + "body-parser": "^1.20.1", "chalk": "^4.0.0", "connect": "^3.7.0", "fs-extra": "9.0.0", @@ -17673,9 +17774,9 @@ } }, "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "requires": { "minimist": "^1.2.0" } @@ -17752,11 +17853,11 @@ } }, "@expo/package-manager": { - "version": "0.0.57", - "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-0.0.57.tgz", - "integrity": "sha512-Y4RpSL9EqaPF+Vd2GrK6r7Xx7Dv0Xdq3AGAD9C0KwV21WqP/scj/dpjxFY+ABwmdhNsFzYXb8fmDyh4tiKenPQ==", + "version": "0.0.60", + "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-0.0.60.tgz", + "integrity": "sha512-MdV7dpFA4aI8HIW0xsW2DBUem5aFKL8+/v5LXKPZuXmYW02/EXPSp7DBJAwow8ULpa3Q2VlYfb46hWPre3hw4A==", "requires": { - "@expo/json-file": "8.2.36", + "@expo/json-file": "^8.2.37", "@expo/spawn-async": "^1.5.0", "ansi-regex": "^5.0.0", "chalk": "^4.0.0", @@ -17768,6 +17869,24 @@ "sudo-prompt": "9.1.1" }, "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@expo/json-file": { + "version": "8.2.37", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-8.2.37.tgz", + "integrity": "sha512-YaH6rVg11JoTS2P6LsW7ybS2CULjf40AbnAHw2F1eDPuheprNjARZMnyHFPkKv7GuxCy+B9GPcbOKgc4cgA80Q==", + "requires": { + "@babel/code-frame": "~7.10.4", + "json5": "^2.2.2", + "write-file-atomic": "^2.3.0" + } + }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -18039,9 +18158,9 @@ "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==" }, "@graphql-typed-document-node/core": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.1.tgz", - "integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.2.tgz", + "integrity": "sha512-9anpBMM9mEgZN4wr2v8wHJI2/u5TnnggewRN6OlvXTTnuVyoY19X6rOv9XTqKRw6dcGKwZsBi8n0kDE2I5i4VA==", "requires": {} }, "@hapi/hoek": { @@ -19134,9 +19253,9 @@ } }, "@sideway/formula": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", - "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" }, "@sideway/pinpoint": { "version": "2.0.0", @@ -19148,6 +19267,11 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" }, + "@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, "@types/geojson": { "version": "7946.0.10", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", @@ -19855,20 +19979,22 @@ "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==" }, "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", + "bytes": "3.1.2", + "content-type": "~1.0.5", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "dependencies": { "debug": { @@ -19883,6 +20009,14 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } } } }, @@ -19982,9 +20116,9 @@ "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==" }, "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, "cacache": { "version": "15.3.0", @@ -20046,7 +20180,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, "requires": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -20459,9 +20592,9 @@ } }, "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" }, "convert-source-map": { "version": "1.9.0", @@ -20597,6 +20730,11 @@ "resolved": "https://registry.npmjs.org/dag-map/-/dag-map-1.0.2.tgz", "integrity": "sha512-+LSAiGFwQ9dRnRdOeaj7g47ZFJcOUPukAP8J3A3fuZ1g9Y44BG+P1sgApjLXTQPOzC4+7S9Wr8kXsfpINM4jpw==" }, + "date-fns": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", + "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==" + }, "dayjs": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.6.tgz", @@ -20616,9 +20754,9 @@ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==" }, "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==" + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" }, "deep-extend": { "version": "0.6.0", @@ -20720,9 +20858,9 @@ "integrity": "sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg==" }, "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" }, "deprecated-react-native-prop-types": { "version": "2.3.0", @@ -20824,6 +20962,31 @@ "once": "^1.4.0" } }, + "engine.io-client": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.3.tgz", + "integrity": "sha512-aXPtgF1JS3RuuKcpSrBtimSjYvrbhKW9froICH4s0F3XQWLxsKNxqzG39nnvQZQnva4CMvUK63T7shevxRyYHw==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3", + "xmlhttprequest-ssl": "~2.0.0" + }, + "dependencies": { + "ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "requires": {} + } + } + }, + "engine.io-parser": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", + "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==" + }, "entities": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", @@ -21533,26 +21696,26 @@ } }, "expo": { - "version": "47.0.8", - "resolved": "https://registry.npmjs.org/expo/-/expo-47.0.8.tgz", - "integrity": "sha512-PGNCIvrnYwHH4TDFsVocq/xhWZ5DW8N3bLkZJPZZgX6VgjtVLNsbZ+0lm1inLCZHP+6xSpSKRccjGHO/QQoMBQ==", + "version": "47.0.13", + "resolved": "https://registry.npmjs.org/expo/-/expo-47.0.13.tgz", + "integrity": "sha512-9VjjGdViCJ9NfWbUE7brkwFBDvKuA35V345vMtHFYNKoGJjXib36yitmawreMDQFv0kMTqTnzc7T2191Pod7Ng==", "requires": { "@babel/runtime": "^7.14.0", - "@expo/cli": "0.4.10", + "@expo/cli": "0.4.11", "@expo/config": "7.0.3", "@expo/config-plugins": "5.0.4", "@expo/vector-icons": "^13.0.0", "babel-preset-expo": "~9.2.2", "cross-spawn": "^6.0.5", "expo-application": "~5.0.1", - "expo-asset": "~8.6.2", + "expo-asset": "~8.7.0", "expo-constants": "~14.0.2", "expo-error-recovery": "~4.0.1", "expo-file-system": "~15.1.1", "expo-font": "~11.0.1", "expo-keep-awake": "~11.0.1", - "expo-modules-autolinking": "1.0.0", - "expo-modules-core": "1.0.3", + "expo-modules-autolinking": "1.0.2", + "expo-modules-core": "1.1.1", "fbemitter": "^3.0.0", "getenv": "^1.0.0", "invariant": "^2.2.4", @@ -21614,9 +21777,9 @@ "requires": {} }, "expo-asset": { - "version": "8.6.2", - "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-8.6.2.tgz", - "integrity": "sha512-XqlXjkuUCEiojbHwbHPjQs1oboRz6w3eV96+9NBD+wb3EUqgAAYY2Do+IWyVCAl8UIFbFi3xzMiqk0Xm9+H8uQ==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-8.7.0.tgz", + "integrity": "sha512-lkoNsHK6vf+outISB6/37SonXcAL6Buw0ycjiwQVFfpOBKpkQa+zw5wm1m3KwjH2txmR3xdIzcpWsJkgovYCvQ==", "requires": { "blueimp-md5": "^2.10.0", "expo-constants": "~14.0.0", @@ -21753,9 +21916,9 @@ } }, "expo-modules-autolinking": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-1.0.0.tgz", - "integrity": "sha512-MoRRkOVMoGUH/Lr8XS6UmBIZT/qrwbRt2IzUBALcM6MWZKtDn9Uct9XgMRxue82FJhRCfy9p1xZJVKHBRo4zEA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-1.0.2.tgz", + "integrity": "sha512-skAUXERKw1gtSw8xsvft9DE0KVhBvw4dujAtgCZoG2l513fN7ds+B5+30ZVgZATMC+EjtlmjKXzhp5QS44DCFA==", "requires": { "chalk": "^4.1.0", "commander": "^7.2.0", @@ -21835,9 +21998,9 @@ } }, "expo-modules-core": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-1.0.3.tgz", - "integrity": "sha512-XqyA5c+zsK+cHDNVBVYu62HLBHyGMG0iWpXVP0bBQJWz0eyg5rcuEqLsnRTmoEz0YnH6QBf/cwRl+FfgnnH5Og==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-1.1.1.tgz", + "integrity": "sha512-+AcaYmaWphIfkBcccu65dyOhWnpOJ3+SQpoI4lI/Plg1nNjOLuBjmrdVvpiJOvkN+CqbNGsJ5Yll8LLk+C107Q==", "requires": { "compare-versions": "^3.4.0", "invariant": "^2.2.4" @@ -22264,7 +22427,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "dev": true, "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -22404,8 +22566,7 @@ "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" }, "has-tostringtag": { "version": "1.0.0", @@ -22501,21 +22662,21 @@ } }, "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" } } }, @@ -23409,9 +23570,9 @@ "dev": true }, "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==" + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" }, "jsonfile": { "version": "4.0.0", @@ -24733,8 +24894,7 @@ "object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", - "dev": true + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" }, "object-keys": { "version": "1.1.1", @@ -24843,9 +25003,9 @@ } }, "open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", "requires": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", @@ -25369,9 +25529,12 @@ "integrity": "sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ==" }, "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "requires": { + "side-channel": "^1.0.4" + } }, "query-string": { "version": "7.1.1", @@ -25400,12 +25563,12 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } @@ -26055,23 +26218,6 @@ } } }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -26090,20 +26236,10 @@ "ee-first": "1.1.1" } }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, "statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" } } }, @@ -26170,9 +26306,9 @@ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" }, "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "shallow-clone": { "version": "3.0.1", @@ -26206,7 +26342,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, "requires": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -26431,6 +26566,26 @@ } } }, + "socket.io-client": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.4.tgz", + "integrity": "sha512-ZpKteoA06RzkD32IbqILZ+Cnst4xewU7ZYK12aS1mzHftFFjpoMz69IuhP/nL25pJfao/amoPI527KnuhFm01g==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.2.3", + "socket.io-parser": "~4.2.1" + } + }, + "socket.io-parser": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", + "integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + } + }, "source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", @@ -26787,18 +26942,23 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, "tar": { - "version": "6.1.12", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.12.tgz", - "integrity": "sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw==", + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", + "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", + "minipass": "^4.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" }, "dependencies": { + "minipass": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.4.tgz", + "integrity": "sha512-lwycX3cBMTvcejsHITUgYj6Gy6A7Nh4Q6h9NP4sTHY1ccJlC7yKzDmiShEHsJ16Jf1nKGDEaiHxiltsJEvk0nQ==" + }, "mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -26956,9 +27116,9 @@ } }, "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, "tr46": { "version": "0.0.3", @@ -26988,9 +27148,9 @@ }, "dependencies": { "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "requires": { "minimist": "^1.2.0" @@ -27051,9 +27211,9 @@ "peer": true }, "ua-parser-js": { - "version": "0.7.32", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.32.tgz", - "integrity": "sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw==" + "version": "0.7.33", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz", + "integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==" }, "uglify-es": { "version": "3.3.9", @@ -27490,6 +27650,11 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-14.0.0.tgz", "integrity": "sha512-ts+B2rSe4fIckR6iquDjsKbQFK2NlUk6iG5nf14mDEyldgoc2nEKZ3jZWMPTxGQwVgToSjt6VGIho1H8/fNFTg==" }, + "xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==" + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index cc5a0df..5f9b527 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@react-native-async-storage/async-storage": "~1.17.3", "@react-navigation/native": "^6.0.13", "@react-navigation/native-stack": "^6.9.1", + "date-fns": "^2.29.3", "expo": "~47.0.8", "expo-clipboard": "~4.0.1", "expo-constants": "~14.0.2", @@ -37,7 +38,8 @@ "react-native-svg": "13.4.0", "react-native-swipe-gestures": "^1.0.5", "react-native-vector-icons": "^9.2.0", - "recoil": "^0.7.5" + "recoil": "^0.7.5", + "socket.io-client": "^4.5.4" }, "devDependencies": { "@babel/core": "^7.12.9", diff --git a/src/Main.js b/src/Main.js index daf3652..180406b 100644 --- a/src/Main.js +++ b/src/Main.js @@ -22,14 +22,14 @@ export default function Main() { const getLocationPermission = async () => { try { await Location.requestForegroundPermissionsAsync(); - } catch (e) { + } catch { console.log('requestForegroundPermissionsAsync failed'); } try { const location = await Location.getCurrentPositionAsync(); dispatcherRef.current.setUserLocation(location); - } catch (e) { + } catch { console.log('No location permissions granted.'); } }; diff --git a/src/components/Home.js b/src/components/Home.js index 4895114..19fd522 100644 --- a/src/components/Home.js +++ b/src/components/Home.js @@ -1,11 +1,16 @@ +import { driverWithoutSerialization } from '@aveq-research/localforage-asyncstorage-driver'; +import localforage from 'localforage'; import React from 'react'; import { StyleSheet, View } from 'react-native'; import { Portal } from 'react-native-paper'; +import { useRecoilValue } from 'recoil'; +import { dispatcherState } from '../state/atoms'; import Map from './Map/Map'; import RouteSelect from './RouteSelect'; import SettingsFAB from './Settings/FAB'; import StopInfo from './StopInfo/StopInfo'; +import Websocket from './Websocket'; const styles = StyleSheet.create({ page: { @@ -22,21 +27,44 @@ const styles = StyleSheet.create({ }, }); +const setup = async () => { + const driver = driverWithoutSerialization(); + await localforage.defineDriver(driver); + await localforage.setDriver(driver._driver); +}; + const Home = () => { + const dispatcher = useRecoilValue(dispatcherState); + const [updatedOnce, setUpdatedOnce] = React.useState(false); + + React.useEffect(() => { + (async () => { + if (!dispatcher || updatedOnce) return; + setUpdatedOnce(true); + await setup(); + dispatcher?.fetchData(); + dispatcher?.fetchFavoriteStops(); + dispatcher?.fetchFavorites(); + dispatcher?.fetchUserSettings(); + })(); + }, []); + return ( <> - - - - - - + + + + + + + + - - - + + + ); }; -export default Home; +export default React.memo(Home); diff --git a/src/components/Map/Map.js b/src/components/Map/Map.js index 0d32399..e4dfe94 100644 --- a/src/components/Map/Map.js +++ b/src/components/Map/Map.js @@ -1,6 +1,5 @@ -import * as Location from 'expo-location'; import React, { useRef } from 'react'; -import { View, Dimensions, AppState } from 'react-native'; +import { View, Dimensions } from 'react-native'; import MapView from 'react-native-maps'; import { useRecoilValue } from 'recoil'; @@ -11,8 +10,6 @@ import RouteLine from './components/RouteLine'; import Stops from './components/Stops'; import Vehicles from './components/Vehicles'; -const FETCH_BUS_SECONDS_INTERVAL = 5000; - const isuCampusRegion = { latitude: 42.02663, longitude: -93.6466, @@ -38,22 +35,6 @@ const Map = () => { if (currentStop) dispatcher?.clearCurrentStop(); }; - const updateVehiclesOnForeground = () => { - const fetchVehiclesInterval = fetchVehiclesOnInterval(route, dispatcher); - - // fetch bus arrivals when app comes back into foreground - const appToForegroundSubscription = AppState.addEventListener('change', (nextAppState) => { - if (nextAppState === 'active') { - fetchVehicle(route, dispatcher); - } - }); - - return () => { - clearInterval(fetchVehiclesInterval); - appToForegroundSubscription.remove(); - }; - }; - const moveMapToUser = () => { if (!location) return; @@ -69,7 +50,6 @@ const Map = () => { } }; - React.useEffect(updateVehiclesOnForeground, [route]); React.useEffect(moveMapToUser, [location]); return ( @@ -88,19 +68,11 @@ const Map = () => { showsIndoors={false} showsTraffic={false}> - + ); }; -const fetchVehicle = (route, dispatcher) => dispatcher?.updateVehicleLocations(route.ID); - -const fetchVehiclesOnInterval = (route, dispatcher) => { - fetchVehicle(route, dispatcher); - - return setInterval(() => fetchVehicle(route, dispatcher), FETCH_BUS_SECONDS_INTERVAL); -}; - export default Map; diff --git a/src/components/Map/components/ImagePin.js b/src/components/Map/components/ImagePin.js index bf606d3..d79e963 100644 --- a/src/components/Map/components/ImagePin.js +++ b/src/components/Map/components/ImagePin.js @@ -25,8 +25,8 @@ class ImagePin extends PureComponent { tracksViewChanges={Platform.OS === 'ios'} onPress={this.onPress} coordinate={{ - latitude: this.props.details.Latitude, - longitude: this.props.details.Longitude, + latitude: this.props.details.latitude, + longitude: this.props.details.longitude, }} style={{ width: 45, height: 45 }} anchor={{ x: 0.5, y: 0.5 }}> diff --git a/src/components/Map/components/RouteLine.js b/src/components/Map/components/RouteLine.js index 960e0b8..824350a 100644 --- a/src/components/Map/components/RouteLine.js +++ b/src/components/Map/components/RouteLine.js @@ -2,35 +2,51 @@ import React from 'react'; import { Polyline } from 'react-native-maps'; import { useRecoilValue } from 'recoil'; -import { routesState } from '../../../state/atoms'; +import { dataState } from '../../../state/atoms'; import { ALL_ROUTES, FAVORITE_ROUTES } from '../../../state/constants'; -import { currentPatternSelector } from '../../../state/selectors'; +import { currentRouteShapeSelector } from '../../../state/selectors'; const RouteLine = ({ route }) => { - let stops = []; - const routes = useRecoilValue(routesState); - const currPattern = useRecoilValue(currentPatternSelector); + let routes = []; + const data = useRecoilValue(dataState); + const currentRouteShape = useRecoilValue(currentRouteShapeSelector); - if (route.ID === FAVORITE_ROUTES) return null; + if (!data || !data.routes || !data.shapes || !data.trips) return; - if (route.ID === ALL_ROUTES) { - stops = Object.values(routes); + if (route.route_id === FAVORITE_ROUTES) return null; + + if (route.route_id === ALL_ROUTES) { + const seenRouteWaypoints = new Set(); + Object.values(data.trips).forEach((trip) => { + const key = data.routes[trip.route_id].route_id; + if (seenRouteWaypoints.has(key)) return; + seenRouteWaypoints.add(key); + routes.push({ + waypoints: data.shapes[trip.shape_id]?.map((a) => ({ + latitude: a.latitude, + longitude: a.longitude, + })), + route: data.routes[trip.route_id], + }); + }); } else { - stops = [route]; + if (!currentRouteShape) return null; + routes = [ + { + waypoints: data.shapes[currentRouteShape]?.map((a) => ({ + latitude: a.latitude, + longitude: a.longitude, + })), + route, + }, + ]; } - - return stops.map((route, i) => { + return routes.map((route, i) => { return ( ); diff --git a/src/components/Map/components/Stops.js b/src/components/Map/components/Stops.js index 2a3420b..0f5980e 100644 --- a/src/components/Map/components/Stops.js +++ b/src/components/Map/components/Stops.js @@ -2,30 +2,33 @@ import React from 'react'; import { useRecoilValue } from 'recoil'; import { dispatcherState, favoriteStopsState } from '../../../state/atoms'; -import { currentRouteStopDetailsState, favoriteStopDetailsState } from '../../../state/selectors'; +import { stopsInCurrentTripSelector, favoriteStopDetailsState } from '../../../state/selectors'; import FavoriteStopImage from '../../icons/FavoriteStopImage'; import RegularStopImage from '../../icons/RegularStopImage'; import ImagePin from './ImagePin'; +/** + * Renders all stops on the map for the given trip. + */ const Stops = () => { - const favoriteStopIDs = useRecoilValue(favoriteStopsState); - const favoriteStopDetails = useRecoilValue(favoriteStopDetailsState); - const stops = useRecoilValue(currentRouteStopDetailsState); - const dispatcher = useRecoilValue(dispatcherState); + const stops = useRecoilValue(stopsInCurrentTripSelector); + const favoriteStops = useRecoilValue(favoriteStopDetailsState); + const favoriteStopIds = useRecoilValue(favoriteStopsState); + const dispatcher = useRecoilValue(dispatcherState); const setActiveStop = (stop) => { dispatcher.setCurrentStop(stop); }; - if (stops && stops.length > 0) { + if (stops) { return stops.map((s, i) => { - const isFavorite = favoriteStopIDs.has(s.RtpiNumber); - // the seprate rendering of ImagePin is required to ensure a rerender of the element + const isFavorite = favoriteStopIds.has(s.stop_id); + // the seprate rendering of ImagePin is required to ensure a re-render of the element // otherwise performance will be 0 fps if (isFavorite) return ( { ); return ( { }); } - if (favoriteStopDetails && favoriteStopDetails.length > 0) { - return favoriteStopDetails.map((s, i) => ( - + if (favoriteStops) { + return favoriteStops.map((s, i) => ( + )); diff --git a/src/components/Map/components/VehicleView.js b/src/components/Map/components/VehicleView.js index 6b68796..f09ad28 100644 --- a/src/components/Map/components/VehicleView.js +++ b/src/components/Map/components/VehicleView.js @@ -1,55 +1,59 @@ import React from 'react'; -import { Image, View } from 'react-native'; +import { Image, View, StyleSheet } from 'react-native'; import { Marker } from 'react-native-maps'; import busImage from '../../../../assets/arrow.png'; +/** + * Show a single vehicle on the map. + */ const VehicleView = ({ details }) => { - const transformMarkerDegrees = calculateMarkerRotation(details); + // bearing may be undefined momentarily when vehicle is first activated on a route + const [bearing, setBearing] = React.useState(null); + + const { latitude, longitude, speed } = details.position; + + React.useEffect(() => { + /* + always update bearing if no bearing is defined for the vehicle + + do not adjust bearing if bearing was reported previously but vehicle is not moving + vehicle has gyroscope on it which can return random directions when stopped + */ + if (details.position.bearing && (bearing === null || speed > 3)) + setBearing(details.position.bearing); + }, [details]); + + if (typeof details?.position?.bearing === 'undefined') return; return ( - - + anchor={centerOfImage}> + + ); }; -const calculateMarkerRotation = (vehicle) => { - // east is the direction before rotation - switch (vehicle.Heading) { - case 'N': - return 270; - case 'NE': - return 315; - case 'E': - return 0; - case 'SE': - return 45; - case 'S': - return 90; - case 'SW': - return 135; - case 'W': - return 180; - case 'NW': - return 225; - } - - return 0; -}; +const centerOfImage = { x: 0.5, y: 0.5 }; + +const styles = StyleSheet.create({ + iconContainer: { + width: 25, + height: 25, + transformOrigin: 'center', + }, + icon: { + width: '100%', + height: '100%', + zIndex: 100, + }, +}); export default React.memo(VehicleView); diff --git a/src/components/Map/components/Vehicles.js b/src/components/Map/components/Vehicles.js index 0145e1b..b835172 100644 --- a/src/components/Map/components/Vehicles.js +++ b/src/components/Map/components/Vehicles.js @@ -1,31 +1,52 @@ import React from 'react'; -import { Platform, ToastAndroid } from 'react-native'; import Toast from 'react-native-root-toast'; import { useRecoilValue } from 'recoil'; -import { vehicleLocationState } from '../../../state/atoms'; -import { isIndividualRoute } from '../../../state/selectors'; +import { + currentRouteRowState, + vehicleLocationState, + vehicleLocationWaitingState, +} from '../../../state/atoms'; +import { isCustomRouteSelector, routeHasVehiclesSelector } from '../../../state/selectors'; import VehicleView from './VehicleView'; +/** + * Render all vehicles on the current route. + */ const Vehicles = () => { - const vehicles = useRecoilValue(vehicleLocationState); - const isRegularRoute = useRecoilValue(isIndividualRoute); + const vehicleLocations = useRecoilValue(vehicleLocationState); + const vehicleLocationsWaiting = useRecoilValue(vehicleLocationWaitingState); + const route = useRecoilValue(currentRouteRowState); + const isCustomRoute = useRecoilValue(isCustomRouteSelector); + const routeHasVehicles = useRecoilValue(routeHasVehiclesSelector); + const [hasReportedNoVehicles, setHasReportedNoVehicles] = React.useState(false); React.useEffect(() => { - if (vehicles === null || vehicles.length > 0) return; - Toast.show('No busses found on route.', { - duration: Toast.durations.SHORT, - position: Toast.positions.BOTTOM, - shadow: true, - animation: true, - hideOnPress: true, - delay: 0, - }); - }, [vehicles]); - - if (!isRegularRoute) return null; - - return vehicles?.map((v) => ); + setHasReportedNoVehicles(false); + }, [route]); + + React.useEffect(() => { + if (!vehicleLocationsWaiting && !routeHasVehicles && !hasReportedNoVehicles && !isCustomRoute) { + setHasReportedNoVehicles(true); + Toast.show('No vehicles found on route.', toastOptions); + } else if (routeHasVehicles) { + setHasReportedNoVehicles(false); + } + }, [vehicleLocations]); + + // there are no busses on favorites or all routes + if (isCustomRoute || !routeHasVehicles) return null; + + return vehicleLocations.map((v) => ); +}; + +const toastOptions = { + duration: Toast.durations.SHORT, + position: Toast.positions.BOTTOM, + shadow: true, + animation: true, + hideOnPress: true, + delay: 0, }; export default React.memo(Vehicles); diff --git a/src/components/RouteSelect.js b/src/components/RouteSelect.js index 442f4ab..16c7479 100644 --- a/src/components/RouteSelect.js +++ b/src/components/RouteSelect.js @@ -1,8 +1,5 @@ -import { driverWithoutSerialization } from '@aveq-research/localforage-asyncstorage-driver'; -import Constants from 'expo-constants'; -import localforage from 'localforage'; import React from 'react'; -import { View, Text, Pressable, Platform, StatusBar } from 'react-native'; +import { View, Text, Pressable, Platform } from 'react-native'; import DropDownPicker from 'react-native-dropdown-picker'; import { Portal } from 'react-native-paper'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; @@ -11,6 +8,7 @@ import { useRecoilValue } from 'recoil'; import { currentRouteRowState, + dataState, dispatcherState, favoriteRoutesState, loadingVehiclesState, @@ -19,13 +17,12 @@ import { ALL_ROUTES, FAVORITE_ROUTES } from '../state/constants'; import { favoriteRoutesOnlyState, routesSortedState } from '../state/selectors'; import LoadingIndicator from './LoadingIndicator'; -const setup = async () => { - const driver = driverWithoutSerialization(); - await localforage.defineDriver(driver); - await localforage.setDriver(driver._driver); -}; +DropDownPicker.modifyTranslation('EN', { + PLACEHOLDER: 'Loading...', +}); const RouteSelect = () => { + const data = useRecoilValue(dataState); const routes = useRecoilValue(routesSortedState); const dispatcher = useRecoilValue(dispatcherState); const favoriteRoutes = useRecoilValue(favoriteRoutesOnlyState); @@ -37,33 +34,24 @@ const RouteSelect = () => { const insets = useSafeAreaInsets(); React.useEffect(() => { - (async () => { - await setup(); - dispatcher?.fetchRoutes(); - dispatcher?.fetchFavoriteStops(); - dispatcher?.fetchStops(); - dispatcher?.fetchFavorites(); - dispatcher?.fetchUserSettings(); - })(); - }, [dispatcher]); - - React.useEffect(() => { - setRouteList([ - { label: 'Favorite Stops', value: FAVORITE_ROUTES, key: 'fav', index: 0 }, - { label: 'All Stops/Routes', value: ALL_ROUTES, key: 'all', index: 1 }, - ...favoriteRoutes.map((r, i) => ({ - label: r.DisplayName, - value: r.ID, - index: i + 2, - key: r.ID + 'f', - })), - ...routes.map((r, i) => ({ - label: r.DisplayName, - value: r.ID, - index: favoriteRoutes.length + i + 2, - key: r.ID, - })), - ]); + if (data?.routes) { + setRouteList([ + { label: 'Favorite Stops', value: FAVORITE_ROUTES, key: 'fav', index: 0 }, + { label: 'All Stops/Routes', value: ALL_ROUTES, key: 'all', index: 1 }, + ...favoriteRoutes.map((r, i) => ({ + label: r.route_long_name, + value: r.route_id, + index: i + 2, + key: r.route_id + 'f', + })), + ...Object.values(routes).map((r, i) => ({ + label: r.route_long_name, + value: r.route_id, + index: favoriteRoutes.length + i + 2, + key: r.route_id, + })), + ]); + } }, [routes, favoriteRoutes]); const open = () => setShowDropDown(true); diff --git a/src/components/Settings/Rules/CreateRule.js b/src/components/Settings/Rules/CreateRule.js new file mode 100644 index 0000000..bc6dc44 --- /dev/null +++ b/src/components/Settings/Rules/CreateRule.js @@ -0,0 +1,179 @@ +import DateTimePicker from '@react-native-community/datetimepicker'; +import React from 'react'; +import { View, StyleSheet, ScrollView } from 'react-native'; +import MapView, { MapCircle } from 'react-native-maps'; +import { Button, Dialog, List, Portal, Text } from 'react-native-paper'; +import { useRecoilValue } from 'recoil'; + +import { currentRuleState, dispatcherState } from '../../../state/atoms'; +import { routesSortedState } from '../../../state/selectors'; +import { isuCampusRegion } from '../../Map/locations'; + +const styles = StyleSheet.create({ + left: { + marginTop: 'auto', + marginBottom: 'auto', + marginLeft: 8, + fontSize: 16, + }, + right: { + marginTop: 'auto', + marginBottom: 'auto', + fontSize: 14, + color: 'grey', + }, +}); + +const RouteDisplay = ({ routeName }) => ( + {routeName || 'Select a Route'} +); + +const CreateRule = ({ navigation }) => { + const [showSelectRoute, setShowSelectRoute] = React.useState(false); + const [showSelectStartTime, setShowSelectStartTime] = React.useState(false); + + const [showSelectEndTime, setShowSelectEndTime] = React.useState(false); + const routes = useRecoilValue(routesSortedState); + const state = useRecoilValue(currentRuleState); + const dispatcher = useRecoilValue(dispatcherState); + React.useEffect(() => { + dispatcher.setCurrentRule({ + time: { start: null, end: null }, + location: null, + stop: null, + route: null, + }); + }, []); + + const openTime = () => { + setShowSelectStartTime(true); + }; + + const openSelectStop = () => { + navigation.push('Settings/CreateRule/SelectStop'); + }; + + const selectRoute = (route) => { + dispatcher.updateCurrentRule('route', route); + setShowSelectRoute(false); + }; + + const doneSelectRoute = () => { + setShowSelectRoute(false); + }; + + const setStartTime = (event, time) => { + setShowSelectStartTime(false); + setShowSelectEndTime(true); + if (event.type === 'set') { + dispatcher.updateCurrentRule('time.start', time); + } + }; + const setEndTime = (event, time) => { + setShowSelectEndTime(false); + if (event.type === 'set') { + dispatcher.updateCurrentRule('time.end', time); + } + }; + + const formatAMPM = (date) => { + let hours = date.getHours(); + let minutes = date.getMinutes(); + const ampm = hours >= 12 ? 'PM' : 'AM'; + hours = hours % 12; + hours = hours ? hours : 12; // the hour '0' should be '12' + minutes = minutes < 10 ? '0' + minutes : minutes; + const strTime = hours + ':' + minutes + ' ' + ampm; + return strTime; + }; + + const formatTime = (time) => { + return `${formatAMPM(time.start)} - ${formatAMPM(time.end)}`; + }; + + return ( + + + Time} + right={() => ( + + {state?.time?.start && state.time.end ? formatTime(state.time) : 'Select a Time'} + + )} + /> + {}} + title="Location" + description={() => ( + + + + + + )} + // right={() => null} + /> + Automatically Show + Stop} + right={() => {state.stop?.Name || 'Select a Stop'}} + /> + setShowSelectRoute(true)} + left={() => Route} + right={() => } + /> + + + + Select a Route + + + selectRoute(null)} /> + {routes.map((route) => ( + selectRoute(route)} /> + ))} + + + + + + + + + {showSelectStartTime ? ( + + ) : null} + {showSelectEndTime ? ( + + ) : null} + + ); +}; + +export default CreateRule; diff --git a/src/components/StopInfo/components/StopDetailsView.js b/src/components/StopInfo/components/StopDetailsView.js index 26d401a..1ad1ece 100644 --- a/src/components/StopInfo/components/StopDetailsView.js +++ b/src/components/StopInfo/components/StopDetailsView.js @@ -1,41 +1,18 @@ import React from 'react'; -import { AppState, ScrollView, View } from 'react-native'; +import { ScrollView, View } from 'react-native'; import { useRecoilValue } from 'recoil'; -import { currentStopState, dispatcherState, loadingArrivalsState } from '../../../state/atoms'; -import LoadingIndicator from '../../LoadingIndicator'; +import { currentStopState } from '../../../state/atoms'; import TopBanner from './TopBanner'; import UpcomingArrivals from './UpcomingArrivals'; const StopDetailsView = () => { const stop = useRecoilValue(currentStopState); - const dispatcher = useRecoilValue(dispatcherState); - const isLoadingArrivals = useRecoilValue(loadingArrivalsState); - - const handleUpdatedStop = () => { - if (!stop) return; - - // fetch upcoming arrivals when app comes back into foreground - const subscription = AppState.addEventListener('change', (nextAppState) => { - if (nextAppState === 'active') { - dispatcher?.fetchUpcomingArrivals(stop); - } - }); - - const interval = fetchUpcomingArrivalsOnInterval(stop, dispatcher); - return () => { - clearInterval(interval); - subscription.remove(); - }; - }; - - React.useEffect(handleUpdatedStop, [stop]); return ( - @@ -44,10 +21,4 @@ const StopDetailsView = () => { ); }; -const fetchUpcomingArrivalsOnInterval = (stop, dispatcher) => { - const fetchUpcomingArrivals = () => dispatcher?.fetchUpcomingArrivals(stop); - fetchUpcomingArrivals(); - return setInterval(fetchUpcomingArrivals, 15 * 1000); -}; - export default StopDetailsView; diff --git a/src/components/StopInfo/components/TopBanner.js b/src/components/StopInfo/components/TopBanner.js index 83fce7c..3d3f192 100644 --- a/src/components/StopInfo/components/TopBanner.js +++ b/src/components/StopInfo/components/TopBanner.js @@ -15,9 +15,22 @@ const TopBanner = ({ stop }) => { const [menuOpen, setMenuOpen] = React.useState(false); - // used so text doesn't disappear when sliding out + // when the drawer closes, the stop name disappears + // this cache holds the name to ensure the ui shows stop name as drawer slides away const [cached, setCached] = React.useState(''); + const storeStopNameInCache = () => { + setCached(stop.stop_name); + }; + + const handleUpdatedStop = () => { + if (!stop) return; + + storeStopNameInCache(); + }; + + React.useEffect(handleUpdatedStop, [stop]); + const toggleFilterSetting = () => { dispatcher?.toggleUserSetting('showFavoriteArrivalsOnly'); }; @@ -30,21 +43,9 @@ const TopBanner = ({ stop }) => { }; const favoriteStop = () => { - dispatcher?.toggleFavoriteStop(stop.RtpiNumber); + dispatcher?.toggleFavoriteStop(stop.stop_id); }; - const storeStopNameInCache = () => { - setCached(stop.Name); - }; - - const handleUpdatedStop = () => { - if (!stop) return; - - storeStopNameInCache(); - }; - - React.useEffect(handleUpdatedStop, [stop]); - return ( { paddingLeft: 24, paddingRight: 12, }}> - {stop?.Name || cached} + {stop?.stop_name || cached} { const upcomingArrivals = useRecoilValue(upcomingArrivalsSorted); - const routes = useRecoilValue(routesState); - const routePatterns = useRecoilValue(routePatternsState); + const data = useRecoilValue(dataState); const dispatcher = useRecoilValue(dispatcherState); + const loadingArrivals = useRecoilValue(loadingArrivalsState); const settings = useRecoilValue(userSettingsState); const favoriteRouteIDs = useRecoilValue(favoriteRoutesState); + if (!data) return; + let renderArrivals = []; - if (upcomingArrivals) { + if (upcomingArrivals && !loadingArrivals) { renderArrivals = upcomingArrivals.map((arrival) => { - const r = routes[routePatterns[arrival.RouteID]]; + const r = data.routes[data.trips[arrival.trip_id].route_id]; + + if (settings?.showFavoriteArrivalsOnly && !favoriteRouteIDs.has(r.route_id)) return null; - if (settings?.showFavoriteArrivalsOnly && !favoriteRouteIDs.has(r.ID)) return null; + let { hours, minutes } = arrival.arrival_time; + let rollover = false; + if (hours >= 24) { + hours %= 24; + rollover = true; + } - const diffMins = getMinsToTime(arrival.ArriveTime); + const d = parse(`${hours}:${minutes}`, 'H:m', Date.now()); + let diffMins = differenceInMinutes(d, Date.now(), { roundingMethod: 'ceil' }); + if (rollover) diffMins += 1440; if (diffMins > 180) return null; return ( { - dispatcher?.updateCurrentRoute(r.ID, false); + dispatcher?.updateCurrentRoute(r.route_id, false); }}> - {r.DisplayName} - {arrival.ArriveTime} ( + {r.route_long_name} - {format(d, 'h:mm a')} ( {diffMins > 1 ? diffMins + ' minutes' : diffMins === 1 ? '1 minute' : 'arriving'}) @@ -59,7 +71,9 @@ const UpcomingArrivals = () => { Upcoming Arrivals{settings.showFavoriteArrivalsOnly && ' (favorite routes)'} - {upcomingArrivals === null ? null : upcomingArrivals.length === 0 ? ( + {loadingArrivals ? ( + Loading... + ) : upcomingArrivals === null ? null : upcomingArrivals.length === 0 ? ( No upcoming arrivals. ) : renderArrivals.filter((arrival) => arrival !== null).length > 0 ? ( renderArrivals @@ -70,37 +84,4 @@ const UpcomingArrivals = () => { ); }; -/** - * Get the minutes until a given time. Assumes the time is not in the past. - * @param {*} arriveTime arrival time in format HH:MM [AM|PM] - * @returns minutes until the given time - */ -const getMinsToTime = (arriveTime) => { - const arrivalDate = new Date(new Date().getTime()); - const minutes = Number.parseInt( - arriveTime.substring(arriveTime.indexOf(':') + 1, arriveTime.indexOf(':') + 3), - 10 - ); - arrivalDate.setMinutes(minutes); - let hours = Number.parseInt(arriveTime.substring(0, arriveTime.indexOf(':')), 10); - - // if hour === 12 and it is am, set hour to zero since it is 12am hour - if (hours === 12 && arriveTime.endsWith('AM')) hours = 0; - - arrivalDate.setHours(hours); - - // if it is currently pm and the arrival time is in the am - if (new Date().getHours() >= 12 && arriveTime.endsWith('AM')) { - // add 24 hours to make it be the next day - arrivalDate.setTime(arrivalDate.getTime() + 24 * 60 * 60 * 1000); - } - // if the given time is in the PM but not in the hour of 12, increase by 12 hours to make date pm - else if (hours < 12 && arriveTime.endsWith('PM')) { - arrivalDate.setTime(arrivalDate.getTime() + 12 * 60 * 60 * 1000); - } - - // get minutes between calculated arrival date and current date - return Math.round((arrivalDate - new Date()) / 1000 / 60); -}; - export default UpcomingArrivals; diff --git a/src/components/Websocket.js b/src/components/Websocket.js new file mode 100644 index 0000000..c7ea081 --- /dev/null +++ b/src/components/Websocket.js @@ -0,0 +1,101 @@ +import React from 'react'; +import { AppState } from 'react-native'; +import { useRecoilValue } from 'recoil'; +import { io } from 'socket.io-client'; + +import { currentStopState, dispatcherState } from '../state/atoms'; +import { currentRoute } from '../state/selectors'; + +const Websocket = ({ children }) => { + const dispatcher = useRecoilValue(dispatcherState); + const [websocket, setWebsocket] = React.useState(null); + const route = useRecoilValue(currentRoute); + const stop = useRecoilValue(currentStopState); + + const appState = React.useRef(AppState.currentState); + + React.useEffect(() => { + console.log('websocket changed'); + const subscription = AppState.addEventListener('change', (nextAppState) => { + if ( + appState.current.match(/inactive|background/) && + nextAppState === 'active' && + websocket.disconnected + ) { + dispatcher.setVehicleLocations(null); + dispatcher.setUpcomingArrivals(null); + dispatcher.setLoading(true); + websocket.connect(); + } + + appState.current = nextAppState; + }); + + return () => { + subscription.remove(); + }; + }, [websocket]); + + React.useEffect(() => { + if (dispatcher) { + setWebsocket((existing) => { + if (existing) { + existing.removeListener('connect'); + existing.on('connect', () => { + dispatcher.setLoading(false); + if (route?.route_id >= 0) subscribeRoute(existing); + if (stop?.stop_id) subscribeStop(existing); + }); + return existing; + } + + const socket = io('https://amesride.demerstech.com/'); + socket.on('connect', () => { + if (route?.route_id >= 0) subscribeRoute(socket); + if (stop?.stop_id) subscribeStop(socket); + }); + socket.on('disconnect', () => { + dispatcher.setLoading(true); + }); + socket.on(`vehicles-on-route`, (message) => { + dispatcher.setVehicleLocations(message); + }); + socket.on(`arrivals`, (message) => { + dispatcher.setUpcomingArrivals(message); + }); + return socket; + }); + } + }, [dispatcher, route, stop]); + + const subscribeRoute = (ws) => { + if (!ws || ws.disconnected) return; + + if (route?.route_id >= 0) { + ws.emit('subscribe-route', route.route_id); + } else { + ws.emit('unsubscribe-route'); + } + }; + + const subscribeStop = (ws) => { + if (!ws || ws.disconnected) return; + if (!stop?.stop_id) { + ws.emit('unsubscribe-arrivals'); + return; + } + dispatcher.setUpcomingArrivals(null); + ws.emit('subscribe-arrivals', stop.stop_id); + }; + + React.useEffect(() => { + subscribeRoute(websocket); + }, [route, websocket]); + React.useEffect(() => { + subscribeStop(websocket); + }, [stop, websocket]); + + return children; +}; + +export default Websocket; diff --git a/src/state/atoms.js b/src/state/atoms.js index 89c25fc..252e7b7 100644 --- a/src/state/atoms.js +++ b/src/state/atoms.js @@ -3,17 +3,16 @@ import { atom } from 'recoil'; import { FAVORITE_ROUTES } from './constants'; import { localForageEffectSet, localForageEffect } from './utilities/localforage/updateEffects'; -export const routesState = atom({ - key: 'routesState-1', - default: {}, - effects: [localForageEffect('routes-1')], -}); - export const vehicleLocationState = atom({ key: 'vehicleLocationState', default: null, }); +export const vehicleLocationWaitingState = atom({ + key: 'vehicleLocationWaitingState', + default: false, +}); + export const currentRouteRowState = atom({ key: 'currentRouteRowState', default: FAVORITE_ROUTES, @@ -51,18 +50,6 @@ export const favoriteStopsState = atom({ effects: [localForageEffectSet('favorite-stops')], }); -export const stopsState = atom({ - key: 'stopsState', - default: {}, - effects: [localForageEffect('stops-state')], -}); - -export const routePatternsState = atom({ - key: 'routePatternsState', - default: {}, - effects: [localForageEffect('route-patterns')], -}); - export const userSettingsState = atom({ key: 'userSettingsState', default: { @@ -80,3 +67,19 @@ export const loadingVehiclesState = atom({ key: 'loadingVehiclesState', default: false, }); + +export const websocketState = atom({ + key: 'websocketState', + default: null, +}); + +export const dataState = atom({ + key: 'dataState', + default: { routes: null, stops: null, trips: null, shapes: null }, + effects: [localForageEffect('dataState')], +}); +export const dataHashState = atom({ + key: 'dataHashState', + default: '', + effects: [localForageEffect('dataHashState')], +}); diff --git a/src/state/dispatcher.js b/src/state/dispatcher.js index be3c503..09fc925 100644 --- a/src/state/dispatcher.js +++ b/src/state/dispatcher.js @@ -1,43 +1,33 @@ import { useRecoilCallback } from 'recoil'; -import { - addFavoriteStop, - removeFavoriteStop, - fetchFavoriteStops, - toggleFavoriteStop, -} from './dispatchers/favoriteStops'; +import { setData, fetchData } from './dispatchers/data'; +import { fetchFavoriteStops, toggleFavoriteStop } from './dispatchers/favoriteStops'; import { addFavorite, removeFavorite, fetchFavorites } from './dispatchers/favorites'; -import { fetchRoutes, updateCurrentRoute } from './dispatchers/routes'; -import { - clearCurrentStop, - setCurrentStop, - fetchUpcomingArrivals, - fetchStops, -} from './dispatchers/stops'; +import { setLoading } from './dispatchers/loading'; +import { updateCurrentRoute } from './dispatchers/routes'; +import { clearCurrentStop, setCurrentStop, setUpcomingArrivals } from './dispatchers/stops'; import { setUserLocation } from './dispatchers/userLocation'; -import { fetchUserSettings, setUserSetting, toggleUserSetting } from './dispatchers/userSettings'; -import { updateVehicleLocations } from './dispatchers/vehicles'; +import { fetchUserSettings, toggleUserSetting } from './dispatchers/userSettings'; +import { setVehicleLocations } from './dispatchers/vehicles'; export const createDispatcher = () => { const methods = { - fetchRoutes, updateCurrentRoute, - updateVehicleLocations, + setVehicleLocations, addFavorite, removeFavorite, fetchFavorites, clearCurrentStop, setCurrentStop, - fetchUpcomingArrivals, + setUpcomingArrivals, setUserLocation, fetchUserSettings, - setUserSetting, toggleUserSetting, - removeFavoriteStop, - addFavoriteStop, fetchFavoriteStops, toggleFavoriteStop, - fetchStops, + setLoading, + setData, + fetchData, }; Object.keys(methods).forEach((key) => { diff --git a/src/state/dispatchers/data.js b/src/state/dispatchers/data.js new file mode 100644 index 0000000..a562f61 --- /dev/null +++ b/src/state/dispatchers/data.js @@ -0,0 +1,58 @@ +import { dataHashState, dataState } from '../atoms'; +import getFromLocalStorage from '../utilities/localforage/getFromLocalStorage'; + +/** + * Set static data about routes/trips/stops/shapes. + */ +export const setData = + ({ set }) => + (data) => { + set(dataState, data.data); + set(dataHashState, data.hash); + }; + +export const fetchData = + ({ set }) => + async () => { + const dataHash = await getFromLocalStorage('dataHashState'); + const data = await getFromLocalStorage('dataState'); + set(dataHashState, dataHash || 'NONE'); + if (data) set(dataState, data); + + const updatedDataState = await getPersistentData(dataHash); + if (updatedDataState) { + set(dataState, updatedDataState.data); + set(dataHashState, updatedDataState.hash); + } + }; + +/** + * Gets persistent data for the app. + * @returns data if the data was found, otherwise null if the latest data is already on device + */ +const getPersistentData = async (hash) => { + try { + // TODO: abstract urls to environment variable + const dataResponse = await fetch('https://amesride.demerstech.com/data?hash=' + hash); + if (dataResponse.status === 204) { + // hash shows latest data already loaded + return null; + } + + if (dataResponse.status === 200) { + return dataResponse.json(); + } + } catch (e) { + console.error('failed to get persistent data'); + console.error(e); + + return new Promise((resolve) => { + setTimeout( + () => { + resolve(getPersistentData()); + }, + hash ? 60 * 1000 : 3 * 1000 + ); + }); + } +}; diff --git a/src/state/dispatchers/loading.js b/src/state/dispatchers/loading.js new file mode 100644 index 0000000..0e0ce8a --- /dev/null +++ b/src/state/dispatchers/loading.js @@ -0,0 +1,7 @@ +import { loadingVehiclesState } from '../atoms'; + +export const setLoading = + ({ set }) => + (isLoading) => { + set(loadingVehiclesState, isLoading); + }; diff --git a/src/state/dispatchers/routes.js b/src/state/dispatchers/routes.js index 58a8e8b..9b4d060 100644 --- a/src/state/dispatchers/routes.js +++ b/src/state/dispatchers/routes.js @@ -1,129 +1,23 @@ import { currentRouteRowState, currentStopState, - routePatternsState, - routesState, - stopsState, upcomingArrivalsState, vehicleLocationState, + vehicleLocationWaitingState, } from '../atoms'; -import getRoutes from '../utilities/request/getRoutes'; -import getStops from '../utilities/request/getStops'; -import getWaypoints from '../utilities/request/getWaypoints'; - -export const fetchRoutes = - ({ set }) => - async () => { - let routes; - try { - routes = await getRoutes(); - } catch (e) { - console.error(e); - return; - } - - if (Object.keys(routes).length === 0) return; - - set(routesState, routes); - - const routePatterns = {}; - Object.values(routes).forEach((route) => { - route.Patterns.forEach((pattern) => { - routePatterns[pattern.ID] = route.ID; - }); - }); - set(routePatternsState, routePatterns); - - const promises = []; - for (const route of Object.values(routes)) { - promises.push(updateStops(routes, route.ID, set)); - promises.push(updateWaypoints(routes, route.ID, set)); - } - await Promise.all(promises); - }; export const updateCurrentRoute = - ({ set, snapshot }) => + ({ set }) => async (route, clearCurrentStop = true) => { set(currentRouteRowState, route); set(vehicleLocationState, null); if (route < 0) return; + set(vehicleLocationWaitingState, true); + if (clearCurrentStop) { set(upcomingArrivalsState, null); set(currentStopState, null); } - - const routes = await snapshot.getLoadable(routesState).contents; - - await updateWaypoints(routes, route, set); - await updateStops(routes, route, set); }; - -const updateWaypoints = async (routes, route, set) => { - if (routes[route].waypoints) return; - let waypoints; - try { - waypoints = await getWaypoints(route); - } catch (e) { - console.error(e); - return; - } - - const result = {}; - - waypoints.forEach((waypoint, i) => { - waypoint = waypoint.map((w) => ({ latitude: w.Latitude, longitude: w.Longitude })); - - result[routes[route].Patterns[i].ID] = waypoint; - }); - - set(routesState, (rs) => { - const updated = { ...rs }; - updated[route] = { ...updated[route], waypoints: result }; - return updated; - }); -}; - -const updateStops = async (routes, route, set) => { - if (routes[route].stops) return; - - const promises = []; - - const allStops = {}; - const result = {}; - - routes[route].Patterns.forEach((pattern) => { - promises.push( - (async () => { - let stops; - try { - stops = await getStops(pattern.ID, route); - } catch (e) { - console.error(e); - return; - } - result[pattern.ID] = stops.map((s) => s.RtpiNumber); - stops.forEach((stop) => { - allStops[stop.RtpiNumber] = stop; - }); - })() - ); - }); - - await Promise.all(promises); - - set(routesState, (rs) => { - const updated = { ...rs }; - updated[route] = { - ...updated[route], - stops: result, - }; - return updated; - }); - - set(stopsState, (current) => { - return { ...current, ...allStops }; - }); -}; diff --git a/src/state/dispatchers/stops.js b/src/state/dispatchers/stops.js index 1f08f90..bc5eacf 100644 --- a/src/state/dispatchers/stops.js +++ b/src/state/dispatchers/stops.js @@ -29,6 +29,25 @@ export const fetchUpcomingArrivals = set(upcomingArrivalsState, arrivals); }; +export const setUpcomingArrivals = + ({ set, snapshot }) => + (websocketPayload) => { + if (!websocketPayload) { + set(upcomingArrivalsState, null); + set(loadingArrivalsState, true); + return; + } + + const stopState = snapshot.getLoadable(currentStopState).contents; + if (!stopState) return; + const currentStopId = stopState.stop_id; + + if (currentStopId === websocketPayload.k) { + set(upcomingArrivalsState, websocketPayload.data ?? []); + set(loadingArrivalsState, false); + } + }; + export const fetchStops = ({ set }) => async () => { diff --git a/src/state/dispatchers/vehicles.js b/src/state/dispatchers/vehicles.js index 6f921b7..30c0b38 100644 --- a/src/state/dispatchers/vehicles.js +++ b/src/state/dispatchers/vehicles.js @@ -1,47 +1,13 @@ -import { loadingVehiclesState, vehicleLocationState } from '../atoms'; -import getVehicleLocations from '../utilities/request/getVehicleLocations'; +import { vehicleLocationState, vehicleLocationWaitingState } from '../atoms'; +import { currentRoute } from '../selectors'; -export const updateVehicleLocations = - ({ set }) => - async (routeID) => { - if (routeID < 0) return; - set(loadingVehiclesState, true); +export const setVehicleLocations = + ({ set, snapshot }) => + async (websocketPayload) => { + const currentRouteId = await snapshot.getLoadable(currentRoute).contents.route_id; - let vehicleLocations; - try { - vehicleLocations = await getVehicleLocations(routeID); - } catch (e) { - // if loading fails, do not turn off loading indicator - console.error(e); - return; + if (currentRouteId === websocketPayload.k) { + set(vehicleLocationState, websocketPayload.data ?? []); + set(vehicleLocationWaitingState, false); } - - set(loadingVehiclesState, false); - set(vehicleLocationState, (current) => determineUpdatedValue(current, vehicleLocations)); }; - -const NO_VEHICLES_FOUND = []; -const VEHICLES_LOADING = null; - -const hasVehicles = (vehicles) => { - return vehicles.length > 0; -}; - -/** - * Determines the new state for vehicleLocationsState. - * - * if new vehicles -> new vehicles; - * else if was loading -> no vehicles; - * else -> use existing vehicles; - * - * @param existingVehicleLocations vehicle locations prior to fetch - * @param updatedVehicleLocations vehicle locations from fetch - * @returns {*[]|*} updated vehicle locations state - */ -const determineUpdatedValue = (existingVehicleLocations, updatedVehicleLocations) => { - if (hasVehicles(updatedVehicleLocations)) return updatedVehicleLocations; - - if (existingVehicleLocations === VEHICLES_LOADING) return NO_VEHICLES_FOUND; - - return existingVehicleLocations; -}; diff --git a/src/state/dispatchers/websocket.js b/src/state/dispatchers/websocket.js new file mode 100644 index 0000000..655e9fa --- /dev/null +++ b/src/state/dispatchers/websocket.js @@ -0,0 +1,7 @@ +import { websocketState } from '../atoms'; + +export const setWebsocket = + ({ set }) => + async (websocket) => { + set(websocketState, websocket); + }; diff --git a/src/state/selectors.js b/src/state/selectors.js index e27747c..2f7cace 100644 --- a/src/state/selectors.js +++ b/src/state/selectors.js @@ -3,63 +3,61 @@ import { selector } from 'recoil'; import { currentRouteRowState, currentStopState, + dataState, favoriteRoutesState, favoriteStopsState, - routesState, - stopsState, upcomingArrivalsState, vehicleLocationState, } from './atoms'; import { ALL_ROUTES } from './constants'; +/** + * Get information about the current route. + * Returns an object with only route_id for non-individual routes. + */ export const currentRoute = selector({ key: 'currentRoute', get: ({ get }) => { - const allRoutes = get(routesState); + const allRoutes = get(dataState); const currentRoute = get(currentRouteRowState); if (currentRoute < 0) { - return { ID: currentRoute }; + return { route_id: currentRoute }; } if (!allRoutes || !currentRoute) { - return {}; + return { route_id: -1 }; } - return { ...allRoutes[currentRoute] }; + return { ...allRoutes.routes[currentRoute] }; }, }); export const routesSortedState = selector({ key: 'routesSortedState', get: ({ get }) => { - const sortRoutes = (a, b) => { - const difference = Number.parseInt(a.ShortName, 10) - Number.parseInt(b.ShortName, 10); - - if (difference !== 0) return difference; - - const directions = ['North', 'East', 'South', 'West']; - const aDir = directions.some((dir) => a.DisplayName.endsWith(dir)); - const bDir = directions.some((dir) => b.DisplayName.endsWith(dir)); - if (aDir && bDir) return a.DisplayName.includes('West') || a.DisplayName.includes('South'); - - return bDir; - }; - - const routes = get(routesState); - return Object.values(routes).sort(sortRoutes); + const data = get(dataState); + if (!data?.routes) return null; + return Object.values(data.routes) + .sort(sortRoutes) + .filter((route) => Boolean(route.trips)); }, }); export const favoriteRoutesOnlyState = selector({ key: 'favoriteRoutesOnlyState', get: ({ get }) => { - const routes = get(routesSortedState); - const favorites = get(favoriteRoutesState); + const data = get(dataState); + const favoriteRouteIds = get(favoriteRoutesState); + + if (!favoriteRouteIds || !data?.routes) return []; + + const favorites = []; + favoriteRouteIds.forEach((routeId) => { + favorites.push(data.routes[routeId]); + }); - if (!favorites) return []; - const result = routes.filter((r) => favorites.has(r.ID)); - return result; + return favorites.sort(sortRoutes); }, }); @@ -67,15 +65,7 @@ export const upcomingArrivalsSorted = selector({ key: 'upcomingArrivalsSorted', get: ({ get }) => { const upcomingArrivals = get(upcomingArrivalsState); - if (!upcomingArrivals) return upcomingArrivals; - - const result = []; - for (const route of upcomingArrivals) { - for (const arrival of route.Arrivals) { - result.push(arrival); - } - } - return result.sort((a, b) => a.Minutes > b.Minutes); + return upcomingArrivals; }, }); @@ -84,79 +74,155 @@ export const isCurrentStopFavorite = selector({ get: ({ get }) => { const currentStop = get(currentStopState); const favoriteStops = get(favoriteStopsState); + if (!currentStop) return false; - return favoriteStops.has(currentStop.RtpiNumber); + return favoriteStops.has(currentStop.stop_id); }, }); export const favoriteStopDetailsState = selector({ key: 'favoriteStopDetailsState', get: ({ get }) => { - const favoriteStopIDs = get(favoriteStopsState); - const allStops = get(stopsState); + const favoriteStopIds = get(favoriteStopsState); + const data = get(dataState); - if (Object.keys(allStops).length === 0) return null; + if (!data?.stops) return null; - const result = []; - for (const stopID of favoriteStopIDs.values()) { - result.push(allStops[stopID]); - } - return result; + // favoriteStopIds is a set with IDs. get the data for each stop + return Array.from(favoriteStopIds).map((stopId) => data.stops[stopId]); }, }); -export const currentRouteStopDetailsState = selector({ - key: 'currentRouteStopDetailsState', +/** + * Get details of each stop on the current trip. + */ +export const stopsInCurrentTripSelector = selector({ + key: 'stopsInCurrentTripSelector', get: ({ get }) => { - const allStops = get(stopsState); const currentRouteID = get(currentRouteRowState); - const allRoutes = get(routesState); - const currentPattern = get(currentPatternSelector); + const currentTrip = get(currentTripSelector); + const data = get(dataState); + + if (!data?.stops) return null; if (currentRouteID === ALL_ROUTES) { - return Object.values(allStops); + return Object.values(data.stops); } - if ( - Object.keys(allStops).length === 0 || - Object.keys(allRoutes).length === 0 || - !allRoutes[currentRouteID]?.stops - ) - return []; + if (!currentTrip) return null; + + // trip.stops only stores the stop_ids, need to map to data + return currentTrip.stops.map((stopId) => data.stops[stopId]); + }, +}); + +/** + * Returns true if this is a fake route (favorites, all routes, etc). + */ +export const isCustomRouteSelector = selector({ + key: 'isCustomRouteSelector', + get: ({ get }) => { + const currentRouteID = get(currentRouteRowState); + + // currentRouteID is below zero for custom routes + return currentRouteID < 0; + }, +}); + +export const currentRouteShapeSelector = selector({ + key: 'currentPatternSelector', + get: ({ get }) => { + /** + * goal: get the shape of the current route + */ + const vehicles = get(vehicleLocationState); + const route = get(currentRoute); + const data = get(dataState); + + // no pattern when on favorites or all routes + if (route < 0) return null; - let pattern = currentPattern; - if (pattern === null) { - pattern = Object.keys(allRoutes[currentRouteID]?.stops)[0]; + if (vehicles && vehicles.length > 0) { + // get the pattern based on the vehicles + return data.trips[vehicles[0].trip].shape_id; } - const result = []; - const stopsInPattern = allRoutes[currentRouteID]?.stops[pattern]; - if (!stopsInPattern) return []; - for (const stopID of stopsInPattern) { - if (allStops[stopID]) result.push(allStops[stopID]); + // if we don't have trips for this route, it is impossible to render a route + if (!route.trips) return null; + + // no busses are loaded, get any trip's line + return data.trips[route.trips[0]].shape_id; + }, +}); + +export const currentTripSelector = selector({ + key: 'currentTripSelector', + get: ({ get }) => { + /** + * goal: get the current trip + */ + const vehicles = get(vehicleLocationState); + const route = get(currentRoute); + const data = get(dataState); + + // no pattern when on favorites or all routes + if (route < 0) return null; + + if (vehicles && vehicles.length > 0) { + // get the pattern based on the vehicles + return data.trips[vehicles[0].trip]; } - return result; + // if we don't have trips for this route, it is impossible to render a route + if (!route.trips) return null; + + // no busses are loaded, get any trip's line + return data.trips[route.trips[0]]; }, }); -export const isIndividualRoute = selector({ - key: 'isIndividualRouteSelector', +const sortRoutes = (a, b) => { + // routes with short_name "A" should come last + if (a.route_short_name === 'A') { + if (b.route_short_name === 'A') { + return a.route_short_name.includes('West') || a.route_short_name.includes('South'); + } + } + const difference = + Number.parseInt(a.route_short_name, 10) - Number.parseInt(b.route_short_name, 10); + + if (difference !== 0) return difference; + + const directions = ['North', 'East', 'South', 'West']; + const aDir = directions.some((dir) => a.route_long_name.endsWith(dir)); + const bDir = directions.some((dir) => b.route_long_name.endsWith(dir)); + if (aDir && bDir) + return a.route_long_name.includes('West') || a.route_long_name.includes('South'); + + return bDir; +}; + +/** + * Does the current route have vehicles on it? + */ +export const routeHasVehiclesSelector = selector({ + key: 'routeHasVehiclesSelector', get: ({ get }) => { - const currentRouteID = get(currentRouteRowState); - return currentRouteID >= 0; + const vehicles = get(vehicleLocationState); + + return vehicles && vehicles.length > 0; }, }); -export const currentPatternSelector = selector({ - key: 'currentPatternSelector', +/** + * Is the application waiting to receive vehicle data for current route? + */ +export const isWaitingForVehicleDataSelector = selector({ + key: 'isWaitingForVehicleDataSelector', get: ({ get }) => { const vehicles = get(vehicleLocationState); - const route = get(currentRoute); - if (!vehicles) return null; - if (vehicles.length > 0) return vehicles[0].PatternId; - return route.Patterns[0].ID; + return vehicles === null; }, });