diff --git a/assets/icon.png b/assets/icon.png index a0b1526f..4edbdfe6 100644 Binary files a/assets/icon.png and b/assets/icon.png differ diff --git a/package-lock.json b/package-lock.json index 32f7ac9f..fc6e8a89 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "@rneui/themed": "^4.0.0-rc.8", "@supabase/supabase-js": "^2.36.0", "@types/validator": "^13.11.5", + "@xenova/transformers": "^2.16.1", "axios": "^1.5.0", "cheerio": "^1.0.0-rc.12", "deprecated-react-native-prop-types": "^4.2.1", @@ -78,8 +79,7 @@ "eslint-config-universe": "^12.0.0", "husky": "^8.0.3", "prettier": "^3.0.3", - "supabase": "^1.110.1", - "typescript": "^5.1.3" + "supabase": "^1.110.1" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -104,12 +104,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" @@ -153,13 +153,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.15.tgz", - "integrity": "sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.4.tgz", + "integrity": "sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==", "dependencies": { - "@babel/types": "^7.22.15", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", + "@babel/types": "^7.24.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" }, "engines": { @@ -462,22 +462,23 @@ } }, "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", + "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.22.16", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz", - "integrity": "sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", + "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", "bin": { "parser": "bin/babel-parser.js" }, @@ -2003,19 +2004,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.20.tgz", - "integrity": "sha512-eU260mPZbU7mZ0N+X10pxXhQFMGTeLb9eFS0mxehS8HZp9o1uSnFeWQuG1UPrlxgA7QoUzFhOnilHDp0AXCyHw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz", + "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.22.15", + "@babel/code-frame": "^7.24.1", + "@babel/generator": "^7.24.1", "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.22.5", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.16", - "@babel/types": "^7.22.19", - "debug": "^4.1.0", + "@babel/parser": "^7.24.1", + "@babel/types": "^7.24.0", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -2023,9 +2024,9 @@ } }, "node_modules/@babel/types": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", - "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", "dependencies": { "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", @@ -3271,9 +3272,9 @@ } }, "node_modules/@expo/metro-runtime": { - "version": "2.2.10", - "resolved": "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-2.2.10.tgz", - "integrity": "sha512-4lEHpOpm7FFwnNsD985eZFtcf32WakD8wSGZmfqc/bkBks076PUb5qIfTgJviGEHF9OAuic+htQi/IcbEdBr9w==", + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-2.2.16.tgz", + "integrity": "sha512-WOUe7ByZsQpFRifyh9WgsjMYrCGHirWA8VvtR5fs+vi0za3yFIaC89wYMvEZILyvn+RIe7Ysln8nzF4xgtnKFg==", "dependencies": { "@bacons/react-views": "^1.1.3", "qs": "^6.10.3" @@ -3682,6 +3683,14 @@ "@hapi/hoek": "^9.0.0" } }, + "node_modules/@huggingface/jinja": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@huggingface/jinja/-/jinja-0.2.2.tgz", + "integrity": "sha512-/KPde26khDUIPkTGU82jdtTW9UAuvUTumCAbFs/7giR0SxsvZC4hru51PBvpijH6BVkHcROcvZM/lpy5h1jRRA==", + "engines": { + "node": ">=18" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.11", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", @@ -4132,13 +4141,13 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" @@ -4153,9 +4162,9 @@ } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "engines": { "node": ">=6.0.0" } @@ -4175,9 +4184,9 @@ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -4791,6 +4800,60 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz", @@ -7263,6 +7326,11 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + }, "node_modules/@types/node": { "version": "20.6.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.3.tgz", @@ -7297,12 +7365,11 @@ } }, "node_modules/@types/react": { - "version": "18.2.22", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.22.tgz", - "integrity": "sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA==", + "version": "18.2.78", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.78.tgz", + "integrity": "sha512-qOwdPnnitQY4xKlKayt42q5W5UQrSHjgoXNVEtxeqdITJ99k4VXJOP3vt8Rkm9HmgJpH50UNU+rlqfkfWOqp0A==", "dependencies": { "@types/prop-types": "*", - "@types/scheduler": "*", "csstype": "^3.0.2" } }, @@ -7350,11 +7417,6 @@ "@types/react": "*" } }, - "node_modules/@types/scheduler": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" - }, "node_modules/@types/semver": { "version": "7.5.3", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.3.tgz", @@ -7715,6 +7777,19 @@ "graphql": "^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0" } }, + "node_modules/@xenova/transformers": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/@xenova/transformers/-/transformers-2.16.1.tgz", + "integrity": "sha512-p2ii7v7oC3Se0PC012dn4vt196GCroaN5ngOYJYkfg0/ce8A5frsrnnnktOBJuejG3bW5Hreb7JZ/KxtUaKd8w==", + "dependencies": { + "@huggingface/jinja": "^0.2.2", + "onnxruntime-web": "1.14.0", + "sharp": "^0.32.0" + }, + "optionalDependencies": { + "onnxruntime-node": "1.14.0" + } + }, "node_modules/@xmldom/xmldom": { "version": "0.7.13", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.13.tgz", @@ -8127,11 +8202,11 @@ } }, "node_modules/axios": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", - "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", "dependencies": { - "follow-redirects": "^1.15.0", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -8149,6 +8224,11 @@ "node": ">= 6" } }, + "node_modules/b4a": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", + "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==" + }, "node_modules/babel-core": { "version": "7.0.0-bridge.0", "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", @@ -8397,6 +8477,38 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/bare-events": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.2.2.tgz", + "integrity": "sha512-h7z00dWdG0PYOQEvChhOSWvOfkIKsdZGkWr083FgN/HyoQuebSew/cgirYqh9SCuy/hRvxc5Vy6Fw8xAmYHLkQ==", + "optional": true + }, + "node_modules/bare-fs": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.2.3.tgz", + "integrity": "sha512-amG72llr9pstfXOBOHve1WjiuKKAMnebcmMbPWDZ7BCevAoJLpugjuAPRsDINEyjT0a6tbaVx3DctkXIRbLuJw==", + "optional": true, + "dependencies": { + "bare-events": "^2.0.0", + "bare-path": "^2.0.0", + "streamx": "^2.13.0" + } + }, + "node_modules/bare-os": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.2.1.tgz", + "integrity": "sha512-OwPyHgBBMkhC29Hl3O4/YfxW9n7mdTr2+SsO29XBWKKJsbgj3mnorDB80r5TiCQgQstgE5ga1qNYrpes6NvX2w==", + "optional": true + }, + "node_modules/bare-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.1.tgz", + "integrity": "sha512-OHM+iwRDRMDBsSW7kl3dO62JyHdBKO3B25FB9vNQBPcGHMo4+eA8Yj41Lfbk3pS/seDY+siNge0LdRTulAau/A==", + "optional": true, + "dependencies": { + "bare-os": "^2.1.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -9630,6 +9742,20 @@ "node": ">=0.10" } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -10342,13 +10468,14 @@ } }, "node_modules/es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", "hasInstallScript": true, "dependencies": { "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", "next-tick": "^1.1.0" }, "engines": { @@ -10985,6 +11112,25 @@ "node": ">= 8" } }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esniff/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -11063,6 +11209,15 @@ "node": ">= 0.6" } }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -11098,6 +11253,14 @@ "node": ">=6" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "engines": { + "node": ">=6" + } + }, "node_modules/expo": { "version": "49.0.23", "resolved": "https://registry.npmjs.org/expo/-/expo-49.0.23.tgz", @@ -11185,9 +11348,9 @@ } }, "node_modules/expo-head": { - "version": "0.0.14", - "resolved": "https://registry.npmjs.org/expo-head/-/expo-head-0.0.14.tgz", - "integrity": "sha512-NvG6dwzi06F2hlYuz1rW/O8B/Us/2lDHTHZv0DJJmSWjAuT3Ja04WtS0QI9Yn5n0bSKKPrEGl4515q1zJ9MI0A==", + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/expo-head/-/expo-head-0.0.20.tgz", + "integrity": "sha512-K0ETFOp/I+Td1T40D8k+Nlk8zCtvUFKTVYiwUhLoCCPf4dGC0zXv/noJLgyZ8jZ+5FJLlrSTpk2Gm9bxJfqkLw==", "dependencies": { "react-helmet-async": "^1.3.0" }, @@ -11348,17 +11511,17 @@ } }, "node_modules/expo-router": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/expo-router/-/expo-router-2.0.8.tgz", - "integrity": "sha512-hKm15AzEmqASgts1qf1UQzjB4ON6iXrV+KsHVicK7L3lb2rLpI6Cj5ayWjjUAQ7GmTw5O2FR/gdlPYil4hALIQ==", + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/expo-router/-/expo-router-2.0.15.tgz", + "integrity": "sha512-6TZKWG6nVne5kGjTPOInAEsSmWy2K4DxXp96OoNUXKoRbJYIZyB++0VQRhXcUCGQSXZRfUa0z2ud8CusF+axNA==", "dependencies": { "@bacons/react-views": "^1.1.3", - "@expo/metro-runtime": "2.2.10", + "@expo/metro-runtime": "2.2.16", "@radix-ui/react-slot": "1.0.1", "@react-navigation/bottom-tabs": "~6.5.7", "@react-navigation/native": "~6.1.6", "@react-navigation/native-stack": "~6.9.12", - "expo-head": "0.0.14", + "expo-head": "0.0.20", "expo-splash-screen": "~0.20.2", "query-string": "7.1.3", "react-helmet-async": "^1.3.0", @@ -11368,10 +11531,6 @@ "peerDependencies": { "@react-navigation/drawer": "^6.5.8", "expo": "^49.0.0", - "expo-constants": "*", - "expo-linking": "*", - "expo-status-bar": "*", - "metro": "~0.76.7", "react-native-gesture-handler": "*", "react-native-reanimated": "*", "react-native-safe-area-context": "*", @@ -11429,6 +11588,11 @@ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==" + }, "node_modules/fast-glob": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", @@ -11702,6 +11866,11 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/flatbuffers": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-1.12.0.tgz", + "integrity": "sha512-c7CZADjRcl6j0PlvFy0ZqXQ67qSEZfrVPynmnL+2zPc+NtMvrF8Y0QceMo7QqnSPc7+uWjUIAbvCQ5WIKlMVdQ==" + }, "node_modules/flatted": { "version": "3.2.9", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", @@ -11722,9 +11891,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", - "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -11795,6 +11964,11 @@ "node": ">= 0.6" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, "node_modules/fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -11942,6 +12116,11 @@ "node": ">=6" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" + }, "node_modules/glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -12059,6 +12238,11 @@ "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, + "node_modules/guid-typescript": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz", + "integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==" + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -14438,6 +14622,11 @@ "node": ">=6" } }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -15285,6 +15474,17 @@ "node": ">=4" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -15381,6 +15581,11 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -15455,6 +15660,11 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -15506,11 +15716,57 @@ "node": ">=12.0.0" } }, + "node_modules/node-abi": { + "version": "3.57.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.57.0.tgz", + "integrity": "sha512-Dp+A9JWxRaKuHP35H77I4kCKesDy5HUDEmScia2FyncMTOXASMyg251F5PhFoDA5uqBrDDffiLpbqnrZmNXW+g==", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abi/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abi/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abi/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" }, + "node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==" + }, "node_modules/node-dir": { "version": "0.1.17", "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz", @@ -15833,6 +16089,46 @@ "node": ">=4" } }, + "node_modules/onnx-proto": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/onnx-proto/-/onnx-proto-4.0.4.tgz", + "integrity": "sha512-aldMOB3HRoo6q/phyB6QRQxSt895HNNw82BNyZ2CMh4bjeKv7g/c+VpAFtJuEMVfYLMbRx61hbuqnKceLeDcDA==", + "dependencies": { + "protobufjs": "^6.8.8" + } + }, + "node_modules/onnxruntime-common": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.14.0.tgz", + "integrity": "sha512-3LJpegM2iMNRX2wUmtYfeX/ytfOzNwAWKSq1HbRrKc9+uqG/FsEA0bbKZl1btQeZaXhC26l44NWpNUeXPII7Ew==" + }, + "node_modules/onnxruntime-node": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/onnxruntime-node/-/onnxruntime-node-1.14.0.tgz", + "integrity": "sha512-5ba7TWomIV/9b6NH/1x/8QEeowsb+jBEvFzU6z0T4mNsFwdPqXeFUM7uxC6QeSRkEbWu3qEB0VMjrvzN/0S9+w==", + "optional": true, + "os": [ + "win32", + "darwin", + "linux" + ], + "dependencies": { + "onnxruntime-common": "~1.14.0" + } + }, + "node_modules/onnxruntime-web": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.14.0.tgz", + "integrity": "sha512-Kcqf43UMfW8mCydVGcX9OMXI2VN17c0p6XvR7IPSZzBf/6lteBzXHvcEVWDPmCKuGombl997HgLqj91F11DzXw==", + "dependencies": { + "flatbuffers": "^1.12.0", + "guid-typescript": "^1.0.9", + "long": "^4.0.0", + "onnx-proto": "^4.0.4", + "onnxruntime-common": "~1.14.0", + "platform": "^1.3.6" + } + }, "node_modules/open": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", @@ -16353,6 +16649,11 @@ "node": ">=4" } }, + "node_modules/platform": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" + }, "node_modules/plist": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", @@ -16422,6 +16723,83 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "node_modules/prebuild-install": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", + "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prebuild-install/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "node_modules/prebuild-install/node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/prebuild-install/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prebuild-install/node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/prebuild-install/node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -16566,6 +16944,31 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/protobufjs": { + "version": "6.11.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz", + "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": ">=13.7.0", + "long": "^4.0.0" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -16659,6 +17062,11 @@ } ] }, + "node_modules/queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==" + }, "node_modules/ramda": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.2.tgz", @@ -16722,9 +17130,9 @@ } }, "node_modules/react-devtools-core": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-4.28.0.tgz", - "integrity": "sha512-E3C3X1skWBdBzwpOUbmXG8SgH6BtsluSMe+s6rRcujNKG1DGi8uIfhdhszkgDpAsMoE55hwqRUzeXCmETDBpTg==", + "version": "4.28.5", + "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-4.28.5.tgz", + "integrity": "sha512-cq/o30z9W2Wb4rzBefjv5fBalHU0rJGZCHAkf/RHSBWSSYwh8PlQTqqOJmgIIbBtpj27T6FIPXeomIjZtCNVqA==", "dependencies": { "shell-quote": "^1.6.1", "ws": "^7" @@ -18090,6 +18498,94 @@ "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" }, + "node_modules/sharp": { + "version": "0.32.6", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.6.tgz", + "integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==", + "hasInstallScript": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.2", + "node-addon-api": "^6.1.0", + "prebuild-install": "^7.1.1", + "semver": "^7.5.4", + "simple-get": "^4.0.1", + "tar-fs": "^3.0.4", + "tunnel-agent": "^0.6.0" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/sharp/node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/sharp/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/sharp/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/sharp/node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/sharp/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -18135,6 +18631,49 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/simple-plist": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-1.3.1.tgz", @@ -18330,6 +18869,18 @@ "node": ">= 0.10.0" } }, + "node_modules/streamx": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.16.1.tgz", + "integrity": "sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ==", + "dependencies": { + "fast-fifo": "^1.1.0", + "queue-tick": "^1.0.1" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" + } + }, "node_modules/strict-uri-encode": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", @@ -18593,16 +19144,16 @@ "integrity": "sha512-rlBo3HU/1zAJUrkY6jNxDOC9eVYliG6nS4JA8u8KAshITd07tafMc/Br7xQwCSseXwJ2iCcHCE8SNWX3q8Z+kw==" }, "node_modules/supabase": { - "version": "1.110.1", - "resolved": "https://registry.npmjs.org/supabase/-/supabase-1.110.1.tgz", - "integrity": "sha512-pQVfbs/n8ZBDuSDv6YJKIH1Uh/QBRxjp6pLW52YkKgjgfwndlKwKuJoPiuWDxBkRG1QXxmCHi3Hk+JeNx9/FRg==", + "version": "1.157.2", + "resolved": "https://registry.npmjs.org/supabase/-/supabase-1.157.2.tgz", + "integrity": "sha512-vmmNNJX7f/gNpCXagdcaPgiKINl2/0IeZSyXd2Jgqal2UlQpeZ5vo6fTzBjZHPZwhKkJecsGoyCxbEKdHPE/yQ==", "dev": true, "hasInstallScript": true, "dependencies": { - "bin-links": "^4.0.1", + "bin-links": "^4.0.3", "https-proxy-agent": "^7.0.2", - "node-fetch": "^3.2.10", - "tar": "6.2.0" + "node-fetch": "^3.3.2", + "tar": "6.2.1" }, "bin": { "supabase": "bin/supabase" @@ -18724,9 +19275,9 @@ } }, "node_modules/tar": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", - "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -18739,6 +19290,29 @@ "node": ">=10" } }, + "node_modules/tar-fs": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.5.tgz", + "integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^2.1.1", + "bare-path": "^2.1.0" + } + }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, "node_modules/tar/node_modules/minipass": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", @@ -19035,6 +19609,17 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", @@ -19161,6 +19746,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/package.json b/package.json index 78b6c810..38187cff 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@rneui/themed": "^4.0.0-rc.8", "@supabase/supabase-js": "^2.36.0", "@types/validator": "^13.11.5", + "@xenova/transformers": "^2.16.1", "axios": "^1.5.0", "cheerio": "^1.0.0-rc.12", "deprecated-react-native-prop-types": "^4.2.1", @@ -82,8 +83,7 @@ "eslint-config-universe": "^12.0.0", "husky": "^8.0.3", "prettier": "^3.0.3", - "supabase": "^1.110.1", - "typescript": "^5.1.3" + "supabase": "^1.110.1" }, "private": true } diff --git a/src/app/(tabs)/_layout.tsx b/src/app/(tabs)/_layout.tsx index de7b449d..eb7592c6 100644 --- a/src/app/(tabs)/_layout.tsx +++ b/src/app/(tabs)/_layout.tsx @@ -34,6 +34,7 @@ function TabNav() { return ( (); @@ -52,14 +52,14 @@ function AuthorScreen() { return ( {isLoading ? ( ) : ( router.back()} /> @@ -139,7 +139,7 @@ function AuthorScreen() { ))} {/* View so there's space between the tab bar and the stories */} - + )} diff --git a/src/app/(tabs)/author/styles.tsx b/src/app/(tabs)/author/styles.tsx index 213b9e7b..dee04f89 100644 --- a/src/app/(tabs)/author/styles.tsx +++ b/src/app/(tabs)/author/styles.tsx @@ -4,7 +4,6 @@ import colors from '../../../styles/colors'; const styles = StyleSheet.create({ authorCardContainer: { - marginTop: 16, marginBottom: 8, flexDirection: 'row', justifyContent: 'flex-start', diff --git a/src/app/(tabs)/genre/index.tsx b/src/app/(tabs)/genre/index.tsx index 87712eb5..ccd0b26c 100644 --- a/src/app/(tabs)/genre/index.tsx +++ b/src/app/(tabs)/genre/index.tsx @@ -14,11 +14,11 @@ import { SafeAreaView } from 'react-native-safe-area-context'; import styles from './styles'; import BackButton from '../../../components/BackButton/BackButton'; +import PreviewCard from '../../../components/PreviewCard/PreviewCard'; import { fetchGenreStoryById } from '../../../queries/genres'; import { fetchStoryPreviewByIds } from '../../../queries/stories'; import { StoryPreview, GenreStories } from '../../../queries/types'; import globalStyles from '../../../styles/globalStyles'; -import PreviewCard from '../../../components/PreviewCard/PreviewCard'; function GenreScreen() { const [genreStoryData, setGenreStoryData] = useState(); @@ -47,13 +47,13 @@ function GenreScreen() { useEffect(() => { const checkTopic = (preview: StoryPreview): boolean => { - if (preview == null || preview.topic == null) return false; + if (preview?.topic == null) return false; if (selectedTopicsForFiltering.length == 0) return true; else return selectedTopicsForFiltering.every(t => preview.topic.includes(t)); }; const checkTone = (preview: StoryPreview): boolean => { - if (preview == null || preview.tone == null) return false; + if (preview?.tone == null) return false; if (selectedTonesForFiltering.length == 0) return true; else return selectedTonesForFiltering.every(t => preview.tone.includes(t)); @@ -202,7 +202,7 @@ function GenreScreen() { const renderGenreHeading = () => { return ( - + {selectedSubgenre === 'All' ? mainGenre : selectedSubgenre} {/* */} @@ -297,13 +297,7 @@ function GenreScreen() { > - - router.push({ - pathname: '/search', - }) - } - /> + router.back()} /> {useMemo(renderGenreHeading, [selectedSubgenre, mainGenre])} {useMemo(renderGenreScrollSelector, [subgenres, selectedSubgenre])} diff --git a/src/app/(tabs)/genre/styles.tsx b/src/app/(tabs)/genre/styles.tsx index 1869ac12..8d69590b 100644 --- a/src/app/(tabs)/genre/styles.tsx +++ b/src/app/(tabs)/genre/styles.tsx @@ -10,7 +10,6 @@ const styles = StyleSheet.create({ container: { paddingHorizontal: 24, width: '100%', - marginTop: 24, flex: 1, }, diff --git a/src/app/(tabs)/home/index.tsx b/src/app/(tabs)/home/index.tsx index b5c7b343..67b4a515 100644 --- a/src/app/(tabs)/home/index.tsx +++ b/src/app/(tabs)/home/index.tsx @@ -1,3 +1,4 @@ +import AsyncStorage from '@react-native-async-storage/async-storage'; import { router } from 'expo-router'; import { useEffect, useState } from 'react'; import { @@ -10,7 +11,6 @@ import { import { SafeAreaView } from 'react-native-safe-area-context'; import styles from './styles'; -import Icon from '../../../../assets/icons'; import ContentCard from '../../../components/ContentCard/ContentCard'; import PreviewCard from '../../../components/PreviewCard/PreviewCard'; import { fetchUsername } from '../../../queries/profiles'; @@ -19,6 +19,7 @@ import { fetchFeaturedStoryPreviews, fetchNewStories, fetchRecommendedStories, + fetchStoryPreviewById, } from '../../../queries/stories'; import { StoryCard, StoryPreview } from '../../../queries/types'; import globalStyles from '../../../styles/globalStyles'; @@ -29,32 +30,97 @@ function HomeScreen() { const [username, setUsername] = useState(''); const [loading, setLoading] = useState(true); const [featuredStories, setFeaturedStories] = useState([]); + const [recentlyViewed, setRecentlyViewed] = useState([]); const [featuredStoriesDescription, setFeaturedStoriesDescription] = useState(''); const [recommendedStories, setRecommendedStories] = useState([]); const [newStories, setNewStories] = useState([]); + const getRecentStory = async () => { + try { + const jsonValue = await AsyncStorage.getItem('GWN_RECENT_STORIES_ARRAY'); + return jsonValue != null ? JSON.parse(jsonValue) : []; + } catch (error) { + console.log(error); + } + }; + + const setRecentStory = async (recentStories: StoryCard[]) => { + try { + const jsonValue = JSON.stringify(recentStories); + await AsyncStorage.setItem('GWN_RECENT_STORIES_ARRAY', jsonValue); + } catch (error) { + console.log(error); + } + }; + + const handleStoryPreviewPressed = (story: StoryPreview) => { + recentlyViewedStacking(story); + router.push({ + pathname: '/story', + params: { storyId: story.id.toString() }, + }); + }; + + const handleStoryCardPressed = async (story: StoryCard) => { + const newStoryArray = await fetchStoryPreviewById(story.id); + recentlyViewedStacking(newStoryArray[0]); + router.push({ + pathname: '/story', + params: { storyId: story.id.toString() }, + }); + }; + + const recentlyViewedStacking = async (story: StoryPreview) => { + const maxArrayLength = 5; + const newRecentlyViewed = [...recentlyViewed]; + + for (let i = 0; i < recentlyViewed.length; i++) { + if (story.id === recentlyViewed[i].id) { + newRecentlyViewed.splice(i, 1); + break; + } + } + + if (newRecentlyViewed.length >= maxArrayLength) { + newRecentlyViewed.splice(-1, 1); + } + + newRecentlyViewed.splice(0, 0, story); + + setRecentStory(newRecentlyViewed); + setRecentlyViewed(newRecentlyViewed); + }; + useEffect(() => { + const getRecommendedStories = async () => { + const recentStoryResponse = await getRecentStory(); + + const recommendedStoriesResponse = + await fetchRecommendedStories(recentStoryResponse); + setRecommendedStories(recommendedStoriesResponse); + }; + (async () => { const [ usernameResponse, featuredStoryResponse, featuredStoryDescriptionResponse, - recommendedStoriesResponse, newStoriesResponse, + recentStoryResponse, ] = await Promise.all([ fetchUsername(user?.id).catch(() => ''), fetchFeaturedStoryPreviews().catch(() => []), fetchFeaturedStoriesDescription().catch(() => ''), - fetchRecommendedStories().catch(() => []), fetchNewStories().catch(() => []), + getRecentStory(), ]); - setUsername(usernameResponse); setFeaturedStories(featuredStoryResponse); setFeaturedStoriesDescription(featuredStoryDescriptionResponse); - setRecommendedStories(recommendedStoriesResponse); setNewStories(newStoriesResponse); + setRecentlyViewed(recentStoryResponse); + await getRecommendedStories(); })().finally(() => { setLoading(false); }); @@ -79,20 +145,18 @@ function HomeScreen() { {username ? `Welcome, ${username}` : 'Welcome!'} - router.push('/settings')}> - - - - {featuredStories.length > 0 && ( Featured Stories - - {featuredStoriesDescription} - - + {featuredStoriesDescription != null && + featuredStoriesDescription.length > 0 && ( + + {featuredStoriesDescription} + + )} + {featuredStories.map(story => ( - router.push({ - pathname: '/story', - params: { storyId: story.id.toString() }, - }) - } + pressFunction={() => handleStoryPreviewPressed(story)} /> ))} @@ -129,17 +188,13 @@ function HomeScreen() { > {recommendedStories.map(story => ( - router.push({ - pathname: '/story', - params: { storyId: story.id.toString() }, - }) - } + pressFunction={() => handleStoryCardPressed(story)} image={story.featured_media} /> ))} @@ -159,17 +214,13 @@ function HomeScreen() { > {newStories.map(story => ( - router.push({ - pathname: '/story', - params: { storyId: story.id.toString() }, - }) - } + pressFunction={() => handleStoryCardPressed(story)} image={story.featured_media} /> ))} diff --git a/src/app/(tabs)/library/index.tsx b/src/app/(tabs)/library/index.tsx index c3ba5b57..2542da9e 100644 --- a/src/app/(tabs)/library/index.tsx +++ b/src/app/(tabs)/library/index.tsx @@ -1,13 +1,157 @@ -import { Text } from 'react-native'; -import { SafeAreaView } from 'react-native-safe-area-context'; +import { Text, View, Pressable, ScrollView } from 'react-native'; +import { useState, useEffect, useMemo } from 'react'; +import { useSession } from '../../../utils/AuthContext'; +import { router } from 'expo-router'; import globalStyles from '../../../styles/globalStyles'; +import styles from './styles'; +import LibraryHeader from '../../../components/LibraryHeader/LibraryHeader'; +import PreviewCard from '../../../components/PreviewCard/PreviewCard'; +import { StoryPreview } from '../../../queries/types'; +import { + fetchUserStoriesFavorites, + fetchUserStoriesReadingList, +} from '../../../queries/savedStories'; +import { FlatList } from 'react-native-gesture-handler'; +import { usePubSub } from '../../../utils/PubSubContext'; function LibraryScreen() { + const { user } = useSession(); + const [favoritesSelected, setFavoritesSelected] = useState(true); + const [readingSelected, setReadingSelected] = useState(false); + const [favoriteStories, setFavoriteStories] = useState([]); + const [readingListStories, setReadingListStories] = useState( + [], + ); + const { channels } = usePubSub(); + let updateReadingListTimeout: NodeJS.Timeout | null = null; + + const favoritesPressed = () => { + setFavoritesSelected(true); + setReadingSelected(false); + }; + + const readingPressed = () => { + setFavoritesSelected(false); + setReadingSelected(true); + }; + + const renderItem = ({ item }: { item: StoryPreview }) => { + return ( + + + router.push({ + pathname: '/story', + params: { storyId: item.id.toString() }, + }) + } + /> + + ); + }; + + useEffect(() => { + if (updateReadingListTimeout) { + clearTimeout(updateReadingListTimeout); + } + + updateReadingListTimeout = setTimeout( + () => + fetchUserStoriesReadingList(user?.id).then(readingList => { + setReadingListStories(readingList); + }), + 5000, + ); + }, [channels]); + + useEffect(() => { + (async () => { + await Promise.all([ + fetchUserStoriesFavorites(user?.id).then(favorites => + setFavoriteStories(favorites), + ), + fetchUserStoriesReadingList(user?.id).then(readingList => { + setReadingListStories(readingList); + }), + ]); + })(); + }, [user]); + return ( - - Library - + + + + + + + + + Favorites + + + + + + + + + Reading List + + + + + + + + {favoritesSelected && + (favoriteStories.length > 0 ? ( + + ) : ( + + + Favorited stories + + + will appear here. + + + ))} + + {readingSelected && + (readingListStories.length > 0 ? ( + + ) : ( + + + Saved stories + + + will appear here. + + + ))} + + ); } diff --git a/src/app/(tabs)/library/styles.ts b/src/app/(tabs)/library/styles.ts index ebaf5c77..5da69e42 100644 --- a/src/app/(tabs)/library/styles.ts +++ b/src/app/(tabs)/library/styles.ts @@ -1,5 +1,43 @@ import { StyleSheet } from 'react-native'; +import colors from '../../../styles/colors'; -const styles = StyleSheet.create({}); +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: 'white', + alignItems: 'flex-start', + justifyContent: 'flex-start', + }, + selector: { + display: 'flex', + width: '100%', + flexDirection: 'row', + justifyContent: 'space-around', + backgroundColor: '#fbfbfb', + marginBottom: 24, + }, + header: { + paddingHorizontal: 24, + marginBottom: 24, + marginTop: 60, + }, + selectedText: { + textAlign: 'center', + color: colors.gwnOrange, + paddingVertical: 8, + }, + unselectedText: { + textAlign: 'center', + color: colors.black, + paddingVertical: 8, + }, + selectedButton: { + borderBottomWidth: 1, + borderBottomColor: colors.gwnOrange, + }, + scrollView: { + width: '100%', + }, +}); export default styles; diff --git a/src/app/(tabs)/search/index.tsx b/src/app/(tabs)/search/index.tsx index fdc5612d..6163689c 100644 --- a/src/app/(tabs)/search/index.tsx +++ b/src/app/(tabs)/search/index.tsx @@ -20,7 +20,12 @@ import PreviewCard from '../../../components/PreviewCard/PreviewCard'; import RecentSearchCard from '../../../components/RecentSearchCard/RecentSearchCard'; import { fetchGenres } from '../../../queries/genres'; import { fetchAllStoryPreviews } from '../../../queries/stories'; -import { StoryPreview, RecentSearch, Genre } from '../../../queries/types'; +import { + StoryPreview, + RecentSearch, + Genre, + StoryPreviewWithPreloadedReactions, +} from '../../../queries/types'; import colors from '../../../styles/colors'; import globalStyles from '../../../styles/globalStyles'; import { GenreType } from '../genre'; @@ -62,9 +67,13 @@ const setRecentStory = async (recentStories: StoryPreview[]) => { }; function SearchScreen() { - const [allStories, setAllStories] = useState([]); + const [allStories, setAllStories] = useState< + StoryPreviewWithPreloadedReactions[] + >([]); const [allGenres, setAllGenres] = useState([]); - const [searchResults, setSearchResults] = useState([]); + const [searchResults, setSearchResults] = useState< + StoryPreviewWithPreloadedReactions[] + >([]); const [search, setSearch] = useState(''); const [filterVisible, setFilterVisible] = useState(false); const [recentSearches, setRecentSearches] = useState([]); @@ -75,9 +84,7 @@ function SearchScreen() { useEffect(() => { (async () => { - fetchAllStoryPreviews().then((stories: StoryPreview[]) => - setAllStories(stories), - ); + fetchAllStoryPreviews().then(stories => setAllStories(stories)); fetchGenres().then((genres: Genre[]) => setAllGenres(genres)); getRecentSearch().then((searches: RecentSearch[]) => setRecentSearches(searches), @@ -99,7 +106,7 @@ function SearchScreen() { return; } - const updatedData = allStories.filter((item: StoryPreview) => { + const updatedData = allStories.filter(item => { const title = `${item.title.toUpperCase()})`; const author = `${item.author_name.toUpperCase()})`; const text_data = text.toUpperCase(); @@ -400,6 +407,7 @@ function SearchScreen() { storyId={item.id} title={item.title} image={item.featured_media} + reactions={item.reactions} author={item.author_name} authorImage={item.author_image} excerpt={item.excerpt} diff --git a/src/app/(tabs)/settings/index.tsx b/src/app/(tabs)/settings/index.tsx index 0434bc6e..5bbc0009 100644 --- a/src/app/(tabs)/settings/index.tsx +++ b/src/app/(tabs)/settings/index.tsx @@ -8,19 +8,20 @@ import { Pressable, Appearance, } from 'react-native'; +import { Icon } from 'react-native-elements'; import { ScrollView } from 'react-native-gesture-handler'; +import DateTimePickerModal from 'react-native-modal-datetime-picker'; import { SafeAreaView } from 'react-native-safe-area-context'; -import { Icon } from 'react-native-elements'; import styles from './styles'; -import colors from '../../../styles/colors'; import AccountDataDisplay from '../../../components/AccountDataDisplay/AccountDataDisplay'; +import BackButton from '../../../components/BackButton/BackButton'; import StyledButton from '../../../components/StyledButton/StyledButton'; import UserSelectorInput from '../../../components/UserSelectorInput/UserSelectorInput'; +import colors from '../../../styles/colors'; import globalStyles from '../../../styles/globalStyles'; import { useSession } from '../../../utils/AuthContext'; import supabase from '../../../utils/supabase'; -import DateTimePickerModal from 'react-native-modal-datetime-picker'; function SettingsScreen() { const { session, signOut } = useSession(); @@ -180,16 +181,12 @@ function SettingsScreen() { edges={['right', 'left', 'top', 'bottom']} > - - - {' - + router.back()} /> + /> diff --git a/src/app/(tabs)/settings/styles.tsx b/src/app/(tabs)/settings/styles.tsx index cded918b..f9476b36 100644 --- a/src/app/(tabs)/settings/styles.tsx +++ b/src/app/(tabs)/settings/styles.tsx @@ -1,4 +1,5 @@ import { StyleSheet } from 'react-native'; + import colors from '../../../styles/colors'; export default StyleSheet.create({ @@ -27,16 +28,6 @@ export default StyleSheet.create({ heading: { paddingBottom: 20, }, - back: { - paddingTop: 30, - paddingBottom: 16, - color: '#797979', - fontSize: 12, - fontWeight: '400', - }, - backText: { - color: colors.darkGrey, - }, staticData: { flexDirection: 'row', flexWrap: 'wrap', diff --git a/src/app/(tabs)/story/index.tsx b/src/app/(tabs)/story/index.tsx index d630daa2..511ac183 100644 --- a/src/app/(tabs)/story/index.tsx +++ b/src/app/(tabs)/story/index.tsx @@ -3,7 +3,6 @@ import React, { useEffect, useState } from 'react'; import { ActivityIndicator, FlatList, - Image, ScrollView, Share, Text, @@ -11,6 +10,7 @@ import { View, useWindowDimensions, } from 'react-native'; +import { Image } from 'expo-image'; import { Button } from 'react-native-paper'; import { RenderHTML } from 'react-native-render-html'; import { SafeAreaView } from 'react-native-safe-area-context'; @@ -18,6 +18,7 @@ import { SafeAreaView } from 'react-native-safe-area-context'; import styles from './styles'; import FavoriteStoryButton from '../../../components/FavoriteStoryButton/FavoriteStoryButton'; import SaveStoryButton from '../../../components/SaveStoryButton/SaveStoryButton'; +import BackButton from '../../../components/BackButton/BackButton'; import { fetchStory } from '../../../queries/stories'; import { Story } from '../../../queries/types'; import colors from '../../../styles/colors'; @@ -76,6 +77,8 @@ function StoryScreen() { ref={scrollRef} showsVerticalScrollIndicator={false} > + router.back()} /> + {story?.title} - - {' - + + router.back()} /> @@ -106,7 +106,7 @@ function ForgotPasswordScreen() { /> - + ); } diff --git a/src/app/auth/forgotPassword/styles.tsx b/src/app/auth/forgotPassword/styles.tsx index c6ccb6b3..83889bff 100644 --- a/src/app/auth/forgotPassword/styles.tsx +++ b/src/app/auth/forgotPassword/styles.tsx @@ -1,12 +1,11 @@ import { StyleSheet } from 'react-native'; + import colors from '../../../styles/colors'; export default StyleSheet.create({ container: { flex: 1, - paddingVertical: 32, - paddingLeft: 30, - paddingRight: 30, + marginHorizontal: 42, }, heading: { paddingBottom: 8, @@ -14,7 +13,6 @@ export default StyleSheet.create({ body: { flex: 1, justifyContent: 'space-between', - paddingHorizontal: 12, }, button: { paddingBottom: 40, @@ -23,14 +21,4 @@ export default StyleSheet.create({ paddingVertical: 18, color: colors.darkGrey, }, - back: { - paddingTop: 30, - paddingBottom: 16, - color: colors.darkGrey, - fontSize: 12, - fontWeight: '400', - }, - backText: { - color: colors.darkGrey, - }, }); diff --git a/src/app/auth/verify/index.tsx b/src/app/auth/verify/index.tsx index a3b9406c..a14cf766 100644 --- a/src/app/auth/verify/index.tsx +++ b/src/app/auth/verify/index.tsx @@ -1,15 +1,15 @@ -import { Link, Redirect, router, useLocalSearchParams } from 'expo-router'; +import { Redirect, router, useLocalSearchParams } from 'expo-router'; import { useState, useRef, useEffect } from 'react'; import { View, Text } from 'react-native'; import OTPTextInput from 'react-native-otp-textinput'; import { SafeAreaView } from 'react-native-safe-area-context'; +import Toast from 'react-native-toast-message'; import styles from './styles'; +import BackButton from '../../../components/BackButton/BackButton'; import colors from '../../../styles/colors'; import globalStyles from '../../../styles/globalStyles'; import { useSession } from '../../../utils/AuthContext'; -import Toast, { BaseToast, BaseToastProps } from 'react-native-toast-message'; -import { Icon } from 'react-native-elements'; function VerificationScreen() { const { user, verifyOtp, resendVerification } = useSession(); @@ -86,51 +86,48 @@ function VerificationScreen() { return ( - - {' - - - - Enter Verification Code{' '} + router.back()} /> + + + Enter Verification Code{' '} + + + + We have sent the verification code to {renderBlurredEmail()} + + + + + + Didn't receive a code? + + Resend Code - - - We have sent the verification code to {renderBlurredEmail()} - - - - - - Didn't receive a code? - - Resend Code + + {errorMessage && ( + + {showX && x} + + {errorMessage} - {errorMessage && ( - - {showX && x} - - {errorMessage} - - - )} - + )} ); } diff --git a/src/app/auth/verify/styles.tsx b/src/app/auth/verify/styles.tsx index 2204f3d4..4ca7af1c 100644 --- a/src/app/auth/verify/styles.tsx +++ b/src/app/auth/verify/styles.tsx @@ -4,16 +4,12 @@ import colors from '../../../styles/colors'; import globalStyles from '../../../styles/globalStyles'; export default StyleSheet.create({ - marginHorizontal: { - marginHorizontal: 38, - }, errorContainer: { paddingTop: 12, flexDirection: 'row', }, title: { lineHeight: 32, - paddingTop: 16, }, sent: { paddingTop: 20, @@ -40,14 +36,7 @@ export default StyleSheet.create({ }, container: { justifyContent: 'flex-start', - marginHorizontal: 0, - }, - back: { - paddingTop: 30, - paddingLeft: 22, - color: '#797979', - fontSize: 12, - fontWeight: '400', + marginHorizontal: 42, }, resendButton: { marginLeft: 8, diff --git a/src/components/AuthorCard/AuthorCard.tsx b/src/components/AuthorCard/AuthorCard.tsx index 99694abd..edec4725 100644 --- a/src/components/AuthorCard/AuthorCard.tsx +++ b/src/components/AuthorCard/AuthorCard.tsx @@ -1,4 +1,5 @@ -import { Image, Text, View } from 'react-native'; +import { Image } from 'expo-image'; +import { Text, View } from 'react-native'; import styles from './styles'; type AuthorCardProps = { diff --git a/src/components/BackButton/BackButton.tsx b/src/components/BackButton/BackButton.tsx index c1ef7854..75fc5ec0 100644 --- a/src/components/BackButton/BackButton.tsx +++ b/src/components/BackButton/BackButton.tsx @@ -6,6 +6,7 @@ import { } from 'react-native'; import styles from './styles'; +import globalStyles from '../../styles/globalStyles'; type BackButtonProps = { pressFunction: (event: GestureResponderEvent) => void; @@ -15,7 +16,7 @@ function BackButton({ pressFunction }: BackButtonProps) { return ( - < Back + <Back ); diff --git a/src/components/BackButton/styles.ts b/src/components/BackButton/styles.ts index 9bda5596..954e8313 100644 --- a/src/components/BackButton/styles.ts +++ b/src/components/BackButton/styles.ts @@ -4,7 +4,8 @@ import colors from '../../styles/colors'; const styles = StyleSheet.create({ backButton: { - paddingTop: 20, + paddingTop: 30, + paddingBottom: 16, flexDirection: 'row', justifyContent: 'flex-start', alignItems: 'center', diff --git a/src/components/ContentCard/ContentCard.tsx b/src/components/ContentCard/ContentCard.tsx index c766751a..c0ac44f7 100644 --- a/src/components/ContentCard/ContentCard.tsx +++ b/src/components/ContentCard/ContentCard.tsx @@ -1,3 +1,5 @@ +import { Image } from 'expo-image'; +import { useEffect, useState } from 'react'; import { GestureResponderEvent, Pressable, @@ -5,14 +7,17 @@ import { View, TouchableOpacity, } from 'react-native'; -import { Image } from 'expo-image'; +import Emoji from 'react-native-emoji'; import styles from './styles'; +import { fetchAllReactionsToStory } from '../../queries/reactions'; +import { Reactions } from '../../queries/types'; import globalStyles from '../../styles/globalStyles'; -import Emoji from 'react-native-emoji'; import SaveStoryButton from '../SaveStoryButton/SaveStoryButton'; +import ReactionDisplay from '../ReactionDisplay/ReactionDisplay'; type ContentCardProps = { + id: number; title: string; author: string; image: string; @@ -22,6 +27,7 @@ type ContentCardProps = { }; function ContentCard({ + id, title, author, image, @@ -29,6 +35,20 @@ function ContentCard({ storyId, pressFunction, }: ContentCardProps) { + const [reactions, setReactions] = useState(); + + useEffect(() => { + (async () => { + const temp = await fetchAllReactionsToStory(id); + if (temp != null) { + setReactions(temp.map(r => r.reaction)); + return; + } + + setReactions([]); + })(); + }, []); + return ( @@ -58,23 +78,7 @@ function ContentCard({ - - - - - - - - - - - {/* heart, clap, muscle, cry, ??? */} - - - 14{/*change number to work*/} - - - + diff --git a/src/components/GenreStoryPreviewCard/GenreStoryPreviewCard.tsx b/src/components/GenreStoryPreviewCard/GenreStoryPreviewCard.tsx index 00b413da..7ea1ba20 100644 --- a/src/components/GenreStoryPreviewCard/GenreStoryPreviewCard.tsx +++ b/src/components/GenreStoryPreviewCard/GenreStoryPreviewCard.tsx @@ -1,10 +1,10 @@ import { GestureResponderEvent, Text, - Image, View, TouchableOpacity, } from 'react-native'; +import { Image } from 'expo-image'; import styles from './styles'; import globalStyles from '../../styles/globalStyles'; diff --git a/src/components/LibraryHeader/LibraryHeader.tsx b/src/components/LibraryHeader/LibraryHeader.tsx new file mode 100644 index 00000000..e9e4a191 --- /dev/null +++ b/src/components/LibraryHeader/LibraryHeader.tsx @@ -0,0 +1,35 @@ +import { View, Text, Image, Pressable } from 'react-native'; +import { router } from 'expo-router'; +import { useEffect, useState } from 'react'; +import Icon from '../../../assets/icons'; + +import styles from './styles'; +import globalStyles from '../../styles/globalStyles'; +import colors from '../../styles/colors'; +import { useSession } from '../../utils/AuthContext'; + +export default function LibraryHeader() { + const { user } = useSession(); + + return ( + + + + + {user?.user_metadata.username} + + + + + router.push('/settings')}> + + + + + + + ); +} diff --git a/src/components/LibraryHeader/styles.ts b/src/components/LibraryHeader/styles.ts new file mode 100644 index 00000000..a3aafbb5 --- /dev/null +++ b/src/components/LibraryHeader/styles.ts @@ -0,0 +1,31 @@ +import { StyleSheet } from 'react-native'; +import colors from '../../styles/colors'; + +const styles = StyleSheet.create({ + container: { + display: 'flex', + flexGrow: 1, + width: '100%', + }, + image: { + height: 51, + width: 51, + borderRadius: 51 / 2, + marginBottom: 12, + }, + textContainer: { + flexDirection: 'row', + }, + username: { + paddingLeft: 12, + }, + horizontal: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + width: '100%', + alignContent: 'center', + }, +}); + +export default styles; diff --git a/src/components/PreviewCard/PreviewCard.tsx b/src/components/PreviewCard/PreviewCard.tsx index 71d6d057..8302d48f 100644 --- a/src/components/PreviewCard/PreviewCard.tsx +++ b/src/components/PreviewCard/PreviewCard.tsx @@ -1,4 +1,6 @@ import * as cheerio from 'cheerio'; +import { Image } from 'expo-image'; +import { useEffect, useState } from 'react'; import { GestureResponderEvent, Pressable, @@ -7,11 +9,13 @@ import { View, } from 'react-native'; import Emoji from 'react-native-emoji'; -import { Image } from 'expo-image'; import styles from './styles'; +import { fetchAllReactionsToStory } from '../../queries/reactions'; +import { Reactions } from '../../queries/types'; import globalStyles from '../../styles/globalStyles'; import SaveStoryButton from '../SaveStoryButton/SaveStoryButton'; +import ReactionDisplay from '../ReactionDisplay/ReactionDisplay'; const placeholderImage = 'https://gwn-uploads.s3.amazonaws.com/wp-content/uploads/2021/10/10120952/Girls-Write-Now-logo-avatar.png'; @@ -22,8 +26,10 @@ type PreviewCardProps = { storyId: number; author: string; authorImage: string; + defaultSavedStoriesState?: boolean; excerpt: { html: string }; tags: string[]; + reactions?: string[] | null; pressFunction: (event: GestureResponderEvent) => void; }; @@ -35,8 +41,28 @@ function PreviewCard({ authorImage, excerpt, tags, + defaultSavedStoriesState = false, pressFunction, + reactions: preloadedReactions = null, }: PreviewCardProps) { + const [reactions, setReactions] = useState( + preloadedReactions, + ); + useEffect(() => { + if (preloadedReactions != null) { + return; + } + + (async () => { + const temp = await fetchAllReactionsToStory(storyId); + if (temp != null) { + setReactions(temp.map(r => r.reaction)); + return; + } + setReactions([]); + })(); + }, []); + return ( @@ -44,8 +70,13 @@ function PreviewCard({ {title} - - + + + + @@ -72,23 +103,7 @@ function PreviewCard({ - - - - - - - - - - - {/* heart, clap, muscle, cry, ??? */} - - - 14{/*change number to work*/} - - - + {(tags?.length ?? 0) > 0 && ( diff --git a/src/components/PreviewCard/savedStoriesIcon.png b/src/components/PreviewCard/savedStoriesIcon.png new file mode 100644 index 00000000..65b2ea6e Binary files /dev/null and b/src/components/PreviewCard/savedStoriesIcon.png differ diff --git a/src/components/PreviewCard/styles.ts b/src/components/PreviewCard/styles.ts index d8d3ef1d..e3e9b927 100644 --- a/src/components/PreviewCard/styles.ts +++ b/src/components/PreviewCard/styles.ts @@ -44,7 +44,9 @@ const styles = StyleSheet.create({ marginBottom: 12, }, title: { - marginBottom: 8, + flex: 1, + alignSelf: 'flex-start', + // marginBottom: 8, }, titleContainer: { paddingTop: 16, @@ -54,8 +56,7 @@ const styles = StyleSheet.create({ borderBottomColor: '#EBEBEB', borderBottomWidth: StyleSheet.hairlineWidth, flexDirection: 'row', - flexGrow: 1, - justifyContent: 'space-between', + flex: 1, }, tag: { paddingHorizontal: 8, diff --git a/src/components/ReactionDisplay/ReactionDisplay.tsx b/src/components/ReactionDisplay/ReactionDisplay.tsx new file mode 100644 index 00000000..0bfb0970 --- /dev/null +++ b/src/components/ReactionDisplay/ReactionDisplay.tsx @@ -0,0 +1,60 @@ +import { Text, View } from 'react-native'; +import styles from './styles'; +import Emoji from 'react-native-emoji'; +import globalStyles from '../../styles/globalStyles'; + +type ReactionDisplayProps = { + reactions: string[]; +}; + +function ReactionDisplay({ reactions }: ReactionDisplayProps) { + const reactionColors: Record = { + heart: '#FFCCCB', + clap: '#FFD580', + cry: '#89CFF0', + hugging_face: '#ffc3bf', + muscle: '#eddcf7', + }; + const defaultColor = reactionColors['heart']; + const setOfReactions = [...reactions]; + setOfReactions.push('heart'); + setOfReactions.push('clap'); + setOfReactions.push('muscle'); + + const reactionDisplay = [...new Set(setOfReactions)].slice(0, 3); + + return ( + + {reactionDisplay.map(reaction => { + return ( + + + + ); + })} + + + {reactions?.length ?? 0} + + + + ); +} + +export default ReactionDisplay; diff --git a/src/components/ReactionDisplay/styles.tsx b/src/components/ReactionDisplay/styles.tsx new file mode 100644 index 00000000..e0d37ab4 --- /dev/null +++ b/src/components/ReactionDisplay/styles.tsx @@ -0,0 +1,27 @@ +import { StyleSheet } from 'react-native'; +import colors from '../../styles/colors'; + +const styles = StyleSheet.create({ + reactions: { + width: 32, + height: 32, + borderRadius: 32 / 2, + borderWidth: 1, + backgroundColor: '#89CFF0', //different per emoji reaction + borderColor: 'white', + marginTop: 10, + marginRight: -5, // -10 + overflow: 'hidden', + justifyContent: 'center', + paddingLeft: 4, + }, + reactionText: { + color: colors.grey, + }, + reactionNumber: { + marginLeft: 16, + marginTop: 16, + }, +}); + +export default styles; diff --git a/src/components/SaveStoryButton/SaveStoryButton.tsx b/src/components/SaveStoryButton/SaveStoryButton.tsx index cf1f3a04..9601c24c 100644 --- a/src/components/SaveStoryButton/SaveStoryButton.tsx +++ b/src/components/SaveStoryButton/SaveStoryButton.tsx @@ -12,14 +12,27 @@ import { usePubSub } from '../../utils/PubSubContext'; type SaveStoryButtonProps = { storyId: number; + defaultState?: boolean | null; }; -export default function SaveStoryButton({ storyId }: SaveStoryButtonProps) { +const saveStoryImage = require('../../../assets/save_story.png'); +const savedStoryImage = require('../../../assets/saved_story.png'); + +export default function SaveStoryButton({ + storyId, + defaultState = null, +}: SaveStoryButtonProps) { const { user } = useSession(); - const [storyIsSaved, setStoryIsSaved] = useState(false); + const [storyIsSaved, setStoryIsSaved] = useState( + defaultState, + ); const { channels, initializeChannel, publish } = usePubSub(); useEffect(() => { + if (defaultState != null) { + return; + } + isStoryInReadingList(storyId, user?.id).then(storyInReadingList => { setStoryIsSaved(storyInReadingList); initializeChannel(storyId); @@ -33,12 +46,6 @@ export default function SaveStoryButton({ storyId }: SaveStoryButtonProps) { } }, [channels[storyId]]); - useEffect(() => { - isStoryInReadingList(storyId, user?.id).then(storyInReadingList => - setStoryIsSaved(storyInReadingList), - ); - }, [storyId]); - const saveStory = async (saved: boolean) => { setStoryIsSaved(saved); publish(storyId, saved); // update other cards with this story diff --git a/src/components/SplashScreen/SplashScreen.tsx b/src/components/SplashScreen/SplashScreen.tsx index 3d791035..d9585ce9 100644 --- a/src/components/SplashScreen/SplashScreen.tsx +++ b/src/components/SplashScreen/SplashScreen.tsx @@ -1,5 +1,6 @@ import React from 'react'; -import { View, Image } from 'react-native'; +import { View } from 'react-native'; +import { Image } from 'expo-image'; import styles from './styles'; diff --git a/src/queries/savedStories.tsx b/src/queries/savedStories.tsx index 605fb44b..fbb499c3 100644 --- a/src/queries/savedStories.tsx +++ b/src/queries/savedStories.tsx @@ -1,4 +1,5 @@ import supabase from '../utils/supabase'; +import { StoryPreview } from './types'; enum SavedList { FAVORITES = 'favorites', @@ -9,21 +10,19 @@ async function fetchUserStories( user_id: string | undefined, name: string | undefined, ) { - const { data: storyObjects, error } = await supabase - .from('saved_stories') - .select('story_id') - .eq('user_id', user_id) - .eq('name', name); + let { data, error } = await supabase.rpc('get_saved_stories_for_user', { + user_id_string: user_id, + saved_list_name: name, + }); if (error) { if (process.env.NODE_ENV !== 'production') { throw new Error( - `An error occured when trying to fetch user saved stories: ${JSON.stringify( + `An error occured when trying to get user saved stories: ${JSON.stringify( error, )}`, ); } - return []; } const storyData = []; @@ -46,7 +45,7 @@ async function fetchUserStories( } } - return storyData; + return data as StoryPreview[]; } export async function fetchUserStoriesFavorites(user_id: string | undefined) { @@ -131,8 +130,8 @@ export async function isStoryInReadingList( storyId: number, userId: string | undefined, ): Promise { - const { data, error } = await supabase.rpc('is_story_saved_for_user', { - list_name: 'reading list', + let { data, error } = await supabase.rpc('is_story_saved_for_user', { + list_name: SavedList.READING_LIST, story_db_id: storyId, user_uuid: userId, }); diff --git a/src/queries/stories.tsx b/src/queries/stories.tsx index 9b8e833b..3efebb7d 100644 --- a/src/queries/stories.tsx +++ b/src/queries/stories.tsx @@ -1,7 +1,16 @@ -import { Story, StoryPreview, StoryCard } from './types'; +import AsyncStorage from '@react-native-async-storage/async-storage'; +import { + Story, + StoryPreview, + StoryCard, + StoryPreviewWithPreloadedReactions, +} from './types'; import supabase from '../utils/supabase'; +import { useState } from 'react'; -export async function fetchAllStoryPreviews(): Promise { +export async function fetchAllStoryPreviews(): Promise< + StoryPreviewWithPreloadedReactions[] +> { const { data, error } = await supabase.rpc('fetch_all_story_previews'); if (error) { @@ -25,7 +34,7 @@ export async function fetchStory(storyId: number): Promise { `An error occured when trying to fetch story ${storyId}: ${error.code}`, ); } else { - return data; + return data as Story[]; } } @@ -57,8 +66,84 @@ export async function fetchFeaturedStoriesDescription(): Promise { } } -export async function fetchRecommendedStories(): Promise { - const { data, error } = await supabase.rpc('fetch_recommended_stories'); +export async function fetchRecommendedStories( + inputStories: StoryPreview[], +): Promise { + if (inputStories.length == 0) { + return []; + } + const storyIDs = inputStories.map(story => story.id); + + //fill storyIDs with 0's if less than 5 ids + for (let n = storyIDs.length; n < 5; n++) { + storyIDs[n] = 0; + } + + //get embedding vectors for each of the inputs + const getStoryEmbeddings = async () => { + const embeddings = inputStories.map(async story => { + const { data, error } = await supabase + .from('stories') + .select('embedding') + .eq('id', story.id); + + if (error) { + console.log(error); + throw new Error( + `An error occured when trying to fetch embeddings: ${error.details}`, + ); + } else { + if (data) { + return data[0].embedding as string; + } + } + }); + + return await Promise.all(embeddings); + }; + + //get embeddings of every story in inputStory + const embeddingsArray = await getStoryEmbeddings(); + const newEmbeddingsArray = []; + for (let k = 0; k < embeddingsArray.length; k++) { + const stringLength = embeddingsArray[k]?.length; + if (stringLength) { + const embedding = embeddingsArray[k]?.substring(1, stringLength - 1); + const formattedEmbedding = embedding?.split(','); + newEmbeddingsArray[k] = formattedEmbedding; + } + } + const embeddingsLength = + newEmbeddingsArray.length > 5 ? 5 : newEmbeddingsArray.length; + + //calculate average embedding vector + const averageEmbedding: number[] = []; + for (let m = 0; m < 384; m++) { + averageEmbedding[m] = 0; + } + for (let i = 0; i < embeddingsLength; i++) { + const vector = newEmbeddingsArray[i]; + if (vector) { + for (let j = 0; j < vector.length; j++) { + const element = parseFloat(vector[j]); + averageEmbedding[j] += element / embeddingsLength; + } + } + } + + const { data, error } = await supabase.rpc( + 'fetch_users_recommended_stories', + { + query_embedding: averageEmbedding, + match_threshold: 0.0, + match_count: 5, + storyid1: storyIDs[0], + storyid2: storyIDs[1], + storyid3: storyIDs[2], + storyid4: storyIDs[3], + storyid5: storyIDs[4], + }, + ); if (error) { console.log(error); diff --git a/src/queries/types.tsx b/src/queries/types.tsx index 60748adc..a7ce63a0 100644 --- a/src/queries/types.tsx +++ b/src/queries/types.tsx @@ -11,6 +11,20 @@ export interface StoryPreview { genre_medium: string[]; } +export interface StoryPreviewWithPreloadedReactions { + id: number; + date: string; + title: string; + excerpt: { html: string }; + featured_media: string; + author_name: string; + author_image: string; + topic: string[]; + tone: string[]; + genre_medium: string[]; + reactions: string[]; +} + export interface Author { id: number; name: string; @@ -70,8 +84,6 @@ export interface GenreStories { } export interface Reactions { - profile_id: number; - story_id: number; - emoji_id: number; - emoji: string; + reaction_id: number; + reaction: string; } diff --git a/src/utils/FilterContext.tsx b/src/utils/FilterContext.tsx index b488e0dd..fa999b8d 100644 --- a/src/utils/FilterContext.tsx +++ b/src/utils/FilterContext.tsx @@ -5,6 +5,7 @@ import React, { useMemo, useReducer, } from 'react'; + import supabase from './supabase'; type FilterAction = diff --git a/tsconfig.json b/tsconfig.json index a0863d0f..e55b0e42 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "expo/tsconfig.base", "compilerOptions": { + "module": "esnext", "strict": true, "jsx": "react-jsx" }