From e0ffb52b2226db8f810c432c3a4a4774b7665c07 Mon Sep 17 00:00:00 2001 From: Justin Lee <91563628+jjstnlee@users.noreply.github.com> Date: Sun, 21 Apr 2024 13:46:58 -0700 Subject: [PATCH 1/4] [chore] Create back button component (#84) * Created back button component * Use rpc to check if password is the same (#80) * Fix worm bug (null tags) (#81) * Fix worm bug (null tags) * Run prettier * Update lint.yml * Run prettier on lint.yml * [bug] Add other ethnicities (#83) * Add other ethnicities * Remove old settings * [update] Update preview card (#82) * accidently forgot to branch * [updated preview card]\n[updated preview and content card emojis/reactions] * chanegs made * Increase margin by 2 --------- Co-authored-by: Marcos Hernandez Co-authored-by: Aditya Pawar * Created Back Button Component --------- Co-authored-by: Aditya Pawar <34043950+adityapawar1@users.noreply.github.com> Co-authored-by: Marcoss28 <69034384+Marcoss28@users.noreply.github.com> Co-authored-by: Marcos Hernandez Co-authored-by: Aditya Pawar --- package-lock.json | 194 ++++++++++-------- src/app/(tabs)/_layout.tsx | 1 + src/app/(tabs)/author/index.tsx | 8 +- src/app/(tabs)/author/styles.tsx | 1 - src/app/(tabs)/genre/index.tsx | 16 +- src/app/(tabs)/genre/styles.tsx | 1 - src/app/(tabs)/settings/index.tsx | 17 +- src/app/(tabs)/settings/styles.tsx | 11 +- src/app/(tabs)/story/index.tsx | 3 + src/app/(tabs)/story/styles.ts | 2 +- src/app/auth/forgotPassword/index.tsx | 20 +- src/app/auth/forgotPassword/styles.tsx | 16 +- src/app/auth/verify/index.tsx | 86 ++++---- src/app/auth/verify/styles.tsx | 13 +- src/components/BackButton/BackButton.tsx | 3 +- src/components/BackButton/styles.ts | 3 +- src/components/PreviewCard/PreviewCard.tsx | 4 + .../PreviewCard/savedStoriesIcon.png | Bin 0 -> 1052 bytes 18 files changed, 192 insertions(+), 207 deletions(-) create mode 100644 src/components/PreviewCard/savedStoriesIcon.png diff --git a/package-lock.json b/package-lock.json index 68298471..839bf147 100644 --- a/package-lock.json +++ b/package-lock.json @@ -103,12 +103,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" @@ -152,13 +152,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": { @@ -461,22 +461,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" }, @@ -2002,19 +2003,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": { @@ -2022,9 +2023,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", @@ -3270,9 +3271,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" @@ -4110,13 +4111,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" @@ -4131,9 +4132,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" } @@ -4153,9 +4154,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" @@ -7275,12 +7276,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" } }, @@ -7328,11 +7328,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", @@ -8105,11 +8100,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" } @@ -10320,13 +10315,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": { @@ -10963,6 +10959,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", @@ -11041,6 +11056,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", @@ -11163,9 +11187,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" }, @@ -11326,17 +11350,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", @@ -11346,10 +11370,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": "*", @@ -11700,9 +11720,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", @@ -16700,9 +16720,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" @@ -18571,16 +18591,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" @@ -18702,9 +18722,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", 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)/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 2995426d..3581936b 100644 --- a/src/app/(tabs)/story/index.tsx +++ b/src/app/(tabs)/story/index.tsx @@ -16,6 +16,7 @@ import { RenderHTML } from 'react-native-render-html'; import { SafeAreaView } from 'react-native-safe-area-context'; import styles from './styles'; +import BackButton from '../../../components/BackButton/BackButton'; import { fetchStory } from '../../../queries/stories'; import { Story } from '../../../queries/types'; import colors from '../../../styles/colors'; @@ -74,6 +75,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..a9b128ae 100644 --- a/src/app/auth/verify/index.tsx +++ b/src/app/auth/verify/index.tsx @@ -1,15 +1,16 @@ import { Link, Redirect, router, useLocalSearchParams } from 'expo-router'; import { useState, useRef, useEffect } from 'react'; import { View, Text } from 'react-native'; +import { Icon } from 'react-native-elements'; import OTPTextInput from 'react-native-otp-textinput'; import { SafeAreaView } from 'react-native-safe-area-context'; +import Toast, { BaseToast, BaseToastProps } 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 +87,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/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/PreviewCard/PreviewCard.tsx b/src/components/PreviewCard/PreviewCard.tsx index 71d6d057..a41e7ab5 100644 --- a/src/components/PreviewCard/PreviewCard.tsx +++ b/src/components/PreviewCard/PreviewCard.tsx @@ -37,6 +37,10 @@ function PreviewCard({ tags, pressFunction, }: PreviewCardProps) { + const saveStory = () => { + console.log("testing '+' icon does something for story " + title); + }; + return ( diff --git a/src/components/PreviewCard/savedStoriesIcon.png b/src/components/PreviewCard/savedStoriesIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..65b2ea6e4dd47e1c590ef1cbe5d7afc29d8fcca7 GIT binary patch literal 1052 zcmV+%1mpXOP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91JfH&r1ONa40RR91JOBUy0E^%0TmS$AhDk(0RA>e5n@@7uFciimr$e`7 zhCjm&P7v1VhMpkVWYbNP6Eryi^aM>$(8;zeN=^{ENH^V(6Qo@wfn;E^B&pr+b1h;E2p>P%^ zpbQq?-QDePZEbx)!+h{wlCB94d0Rp7Dd53i@ClWUJ+Q82S)K8Cd|s(k*x2~g@AvBv zl1CH3Rpd;qVzKz~->L8O;o+ePUuKrbW`fVi zqM-RzouJ&`-(P#ciMQ+o$~yrUE$teUld(|NMSdNuoR(_(yQfn;M+^dj^sVI7We(n$i%k7S&sd<5^duoYdS(=jKw9*6>g(U-yqNOC) z$t;{?>qU`$<1GA2r@nU*mXIMN=))V14f=4r*LT@(7=xLG;_HeuMsDMM!AIJIj+ z-`aK91W&7fuzLz$U`I&ND}KE^SwwMKU=uto54IuPy_rRDh0p@0&C}8Xm)bw*$iH1o zujaJQW@&S55sxI9h2znyIlX2UHpdq6NTS^USeB<1wpTUrpfqe!iaLA_ZvBkld~lhZ zpF%t`^;e5_9bT5F6}DG3@u235?{~1Uza|=*>cZb3SXT?YEKe(JugVe5m2@KfhX)Ho z@W>Wszd=xStsT;;;yUxe!J7-$vX1B$+n4|4^{FA9&C=_%S$Gv%UgtA13$IiA zt9W*1;ZQ8bmrI=+zT z9%pIy_a(shlL!-0X8zg|h$sEc;L`8BK;WetRm=0VB}580$ppr4fxLNn~&jVDeU zEM9H+KsFT*Zf$RGk1(8%JPkB`<2~{0FeYC?Y7t~69vrU$oKh3CHY3NAPD{&z$sM_P z-@%0@)71V7V0ptG@80a)V&HhGRB|UaHZVDHrOD-TdyttEaD%eK!k;1k#DG)4tbieX zh|K@q4~vtSMgTu5m&+Z$@H4>bCc$B4&fHZTbyaS1&QS@A_V9h}K0ZF4)oQg-P~N|w WSfpXwp?D_%0000 Date: Sun, 21 Apr 2024 14:30:32 -0700 Subject: [PATCH 2/4] [react] Fetch reactions to story for content card (#87) * accidently forgot to branch * mergong * reactions added to the PreviewCard and ContentCard * prettier problems * minor change * fixed small error * Improve performence * Preload reactions * Clean up code * Make content card use string[] * Add null checks for preview length * Add reactions display component * Run prettier --------- Co-authored-by: Marcos Hernandez Co-authored-by: Aditya Pawar Co-authored-by: Aditya Pawar <34043950+adityapawar1@users.noreply.github.com> --- src/app/(tabs)/author/index.tsx | 4 +- src/app/(tabs)/home/index.tsx | 2 + src/app/(tabs)/search/index.tsx | 22 ++++--- src/app/(tabs)/story/index.tsx | 2 +- src/app/auth/verify/index.tsx | 5 +- src/components/AuthorCard/AuthorCard.tsx | 3 +- src/components/ContentCard/ContentCard.tsx | 42 +++++++------ .../GenreStoryPreviewCard.tsx | 2 +- src/components/PreviewCard/PreviewCard.tsx | 46 +++++++------- .../ReactionDisplay/ReactionDisplay.tsx | 60 +++++++++++++++++++ src/components/ReactionDisplay/styles.tsx | 27 +++++++++ .../SaveStoryButton/SaveStoryButton.tsx | 20 ++++--- src/components/SplashScreen/SplashScreen.tsx | 3 +- src/queries/stories.tsx | 13 +++- src/queries/types.tsx | 20 +++++-- src/utils/FilterContext.tsx | 1 + tsconfig.json | 1 + 17 files changed, 202 insertions(+), 71 deletions(-) create mode 100644 src/components/ReactionDisplay/ReactionDisplay.tsx create mode 100644 src/components/ReactionDisplay/styles.tsx diff --git a/src/app/(tabs)/author/index.tsx b/src/app/(tabs)/author/index.tsx index 542738f8..1feee80a 100644 --- a/src/app/(tabs)/author/index.tsx +++ b/src/app/(tabs)/author/index.tsx @@ -1,8 +1,8 @@ import * as cheerio from 'cheerio'; import { useLocalSearchParams, router } from 'expo-router'; -import { decode } from 'html-entities'; import { useEffect, useState } from 'react'; -import { ActivityIndicator, ScrollView, View, Text, Image } from 'react-native'; +import { ActivityIndicator, ScrollView, View, Text } from 'react-native'; +import { Image } from 'expo-image'; import { SafeAreaView } from 'react-native-safe-area-context'; import styles from './styles'; diff --git a/src/app/(tabs)/home/index.tsx b/src/app/(tabs)/home/index.tsx index b5c7b343..2b262871 100644 --- a/src/app/(tabs)/home/index.tsx +++ b/src/app/(tabs)/home/index.tsx @@ -129,6 +129,7 @@ function HomeScreen() { > {recommendedStories.map(story => ( {newStories.map(story => ( { }; 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)/story/index.tsx b/src/app/(tabs)/story/index.tsx index 3581936b..50602db8 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'; diff --git a/src/app/auth/verify/index.tsx b/src/app/auth/verify/index.tsx index a9b128ae..a14cf766 100644 --- a/src/app/auth/verify/index.tsx +++ b/src/app/auth/verify/index.tsx @@ -1,10 +1,9 @@ -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 { Icon } from 'react-native-elements'; import OTPTextInput from 'react-native-otp-textinput'; import { SafeAreaView } from 'react-native-safe-area-context'; -import Toast, { BaseToast, BaseToastProps } from 'react-native-toast-message'; +import Toast from 'react-native-toast-message'; import styles from './styles'; import BackButton from '../../../components/BackButton/BackButton'; 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/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/PreviewCard/PreviewCard.tsx b/src/components/PreviewCard/PreviewCard.tsx index a41e7ab5..f561cd48 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'; @@ -24,6 +28,7 @@ type PreviewCardProps = { authorImage: string; excerpt: { html: string }; tags: string[]; + reactions?: string[] | null; pressFunction: (event: GestureResponderEvent) => void; }; @@ -36,10 +41,25 @@ function PreviewCard({ excerpt, tags, pressFunction, + reactions: preloadedReactions = null, }: PreviewCardProps) { - const saveStory = () => { - console.log("testing '+' icon does something for story " + title); - }; + 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 ( @@ -76,23 +96,7 @@ function PreviewCard({ - - - - - - - - - - - {/* heart, clap, muscle, cry, ??? */} - - - 14{/*change number to work*/} - - - + {(tags?.length ?? 0) > 0 && ( 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 c4d43315..be44ebb9 100644 --- a/src/components/SaveStoryButton/SaveStoryButton.tsx +++ b/src/components/SaveStoryButton/SaveStoryButton.tsx @@ -11,17 +11,27 @@ import { TouchableOpacity } from 'react-native-gesture-handler'; type SaveStoryButtonProps = { storyId: number; + defaultState?: boolean | null; }; const saveStoryImage = require('../../../assets/save_story.png'); const savedStoryImage = require('../../../assets/saved_story.png'); -export default function SaveStoryButton({ storyId }: SaveStoryButtonProps) { +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); @@ -35,12 +45,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/stories.tsx b/src/queries/stories.tsx index 9b8e833b..e48dc51b 100644 --- a/src/queries/stories.tsx +++ b/src/queries/stories.tsx @@ -1,7 +1,14 @@ -import { Story, StoryPreview, StoryCard } from './types'; +import { + Story, + StoryPreview, + StoryCard, + StoryPreviewWithPreloadedReactions, +} from './types'; import supabase from '../utils/supabase'; -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 +32,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[]; } } 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" } From b88af9e9e62571469e50f4eb6a31607797f70172 Mon Sep 17 00:00:00 2001 From: Kyle Ramachandran <156966341+kylezryr@users.noreply.github.com> Date: Sun, 21 Apr 2024 15:00:29 -0700 Subject: [PATCH 3/4] [rec] Implement recommendation algorithm (#88) * working on embeddings * working on recently viewed * recommended stories bug * fix(recommendations): uses query response directly for recommendations * finished implementing recommendation alg * Small merge changes --------- Co-authored-by: Kyle Ramachandran Co-authored-by: Noah Hernandez <63211322+oahnh@users.noreply.github.com> Co-authored-by: Aditya Pawar <34043950+adityapawar1@users.noreply.github.com> Co-authored-by: Aditya Pawar --- package-lock.json | 570 +++++++++++++++++++++++++++++++++- package.json | 4 +- src/app/(tabs)/home/index.tsx | 100 ++++-- src/queries/savedStories.tsx | 2 +- src/queries/stories.tsx | 82 ++++- 5 files changed, 727 insertions(+), 31 deletions(-) diff --git a/package-lock.json b/package-lock.json index 839bf147..da49b463 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", @@ -77,8 +78,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": { @@ -3682,6 +3682,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", @@ -4770,6 +4778,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", @@ -7242,6 +7304,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", @@ -7688,6 +7755,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", @@ -8122,6 +8202,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", @@ -8370,6 +8455,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", @@ -9603,6 +9720,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", @@ -11100,6 +11231,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", @@ -11427,6 +11566,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", @@ -11700,6 +11844,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", @@ -11793,6 +11942,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", @@ -11940,6 +12094,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", @@ -12057,6 +12216,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", @@ -14436,6 +14600,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", @@ -15283,6 +15452,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", @@ -15379,6 +15559,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", @@ -15453,6 +15638,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", @@ -15504,11 +15694,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", @@ -15831,6 +16067,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", @@ -16351,6 +16627,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", @@ -16420,6 +16701,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", @@ -16564,6 +16922,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", @@ -16657,6 +17040,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", @@ -18088,6 +18476,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", @@ -18133,6 +18609,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", @@ -18328,6 +18847,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", @@ -18737,6 +19268,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", @@ -19033,6 +19587,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", @@ -19159,6 +19724,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 d15d3b50..45a2c09d 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", @@ -81,8 +82,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)/home/index.tsx b/src/app/(tabs)/home/index.tsx index 2b262871..ddca1a32 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 { @@ -19,6 +20,7 @@ import { fetchFeaturedStoryPreviews, fetchNewStories, fetchRecommendedStories, + fetchStoryPreviewById, } from '../../../queries/stories'; import { StoryCard, StoryPreview } from '../../../queries/types'; import globalStyles from '../../../styles/globalStyles'; @@ -29,32 +31,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); }); @@ -105,12 +172,7 @@ function HomeScreen() { tags={story.genre_medium .concat(story.tone) .concat(story.topic)} - pressFunction={() => - router.push({ - pathname: '/story', - params: { storyId: story.id.toString() }, - }) - } + pressFunction={() => handleStoryPreviewPressed(story)} /> ))} @@ -130,17 +192,12 @@ function HomeScreen() { {recommendedStories.map(story => ( - router.push({ - pathname: '/story', - params: { storyId: story.id.toString() }, - }) - } + pressFunction={() => handleStoryCardPressed(story)} image={story.featured_media} /> ))} @@ -161,17 +218,12 @@ 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/queries/savedStories.tsx b/src/queries/savedStories.tsx index 0416ecb8..a9578b4a 100644 --- a/src/queries/savedStories.tsx +++ b/src/queries/savedStories.tsx @@ -132,7 +132,7 @@ export async function isStoryInReadingList( userId: string | undefined, ): Promise { let { data, error } = await supabase.rpc('is_story_saved_for_user', { - list_name: 'reading list', + 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 e48dc51b..3efebb7d 100644 --- a/src/queries/stories.tsx +++ b/src/queries/stories.tsx @@ -1,3 +1,4 @@ +import AsyncStorage from '@react-native-async-storage/async-storage'; import { Story, StoryPreview, @@ -5,6 +6,7 @@ import { StoryPreviewWithPreloadedReactions, } from './types'; import supabase from '../utils/supabase'; +import { useState } from 'react'; export async function fetchAllStoryPreviews(): Promise< StoryPreviewWithPreloadedReactions[] @@ -64,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); From 14dbd0df9630d8f82f08dd47067ae72f3b9298b6 Mon Sep 17 00:00:00 2001 From: Kyle Ramachandran <156966341+kylezryr@users.noreply.github.com> Date: Sun, 21 Apr 2024 16:22:50 -0700 Subject: [PATCH 4/4] [saved] Saved stories page (#73) * initial commit * Fix width bugs * styling * adding preview cards * merging saved stories into branch * styling selector bar * finished styling * removed unused folders * Fix title preview card overflow * Fix featured stories extra space * Revert and useMemo to cache * Implement flatlist * Add default state to saved story button * Run prettier * Auto update reading list page * Finish library screen --------- Co-authored-by: Kyle Ramachandran Co-authored-by: Aditya Pawar Co-authored-by: Aditya Pawar <34043950+adityapawar1@users.noreply.github.com> --- assets/icon.png | Bin 22380 -> 60459 bytes src/app/(tabs)/home/index.tsx | 17 +- src/app/(tabs)/library/index.tsx | 154 +++++++++++++++++- src/app/(tabs)/library/styles.ts | 40 ++++- .../LibraryHeader/LibraryHeader.tsx | 35 ++++ src/components/LibraryHeader/styles.ts | 31 ++++ src/components/PreviewCard/PreviewCard.tsx | 11 +- src/components/PreviewCard/styles.ts | 7 +- src/queries/savedStories.tsx | 38 ++--- 9 files changed, 285 insertions(+), 48 deletions(-) create mode 100644 src/components/LibraryHeader/LibraryHeader.tsx create mode 100644 src/components/LibraryHeader/styles.ts diff --git a/assets/icon.png b/assets/icon.png index a0b1526fc7b78680fd8d733dbc6113e1af695487..4edbdfe6eb1d5146d177c72dd9b51c1feb07be06 100644 GIT binary patch literal 60459 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 !apDUHXfDqwbVsPN#O;gOU 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 literal 22380 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 diff --git a/src/app/(tabs)/home/index.tsx b/src/app/(tabs)/home/index.tsx index ddca1a32..67b4a515 100644 --- a/src/app/(tabs)/home/index.tsx +++ b/src/app/(tabs)/home/index.tsx @@ -11,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'; @@ -146,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 => ( ([]); + 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/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 f561cd48..8302d48f 100644 --- a/src/components/PreviewCard/PreviewCard.tsx +++ b/src/components/PreviewCard/PreviewCard.tsx @@ -26,6 +26,7 @@ type PreviewCardProps = { storyId: number; author: string; authorImage: string; + defaultSavedStoriesState?: boolean; excerpt: { html: string }; tags: string[]; reactions?: string[] | null; @@ -40,6 +41,7 @@ function PreviewCard({ authorImage, excerpt, tags, + defaultSavedStoriesState = false, pressFunction, reactions: preloadedReactions = null, }: PreviewCardProps) { @@ -68,8 +70,13 @@ function PreviewCard({ {title} - - + + + + 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/queries/savedStories.tsx b/src/queries/savedStories.tsx index a9578b4a..5ab847d7 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,44 +10,27 @@ 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 []; } - let storyData = []; - for (const storyObject of storyObjects) { - const storyId = storyObject['story_id']; - const { data, error } = await supabase.rpc('fetch_story', { - input_id: storyId, - }); - - if (error || data.length == 0) { - if (process.env.NODE_ENV !== 'production') { - throw new Error( - `An error occured when trying to use rpc to get story data: ${JSON.stringify( - error, - )}`, - ); - } - } else { - storyData.push(data[0]); - } - } + // console.log(data[0]); + // console.log("As preview:"); + // console.log(data[0] as StoryPreview) + // console.log(data as StoryPreview[]); - return storyData; + return data as StoryPreview[]; } export async function fetchUserStoriesFavorites(user_id: string | undefined) {