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 74431c3..d006ca1 100644 Binary files a/assets/arrow.png and b/assets/arrow.png differ 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; }, });