From e67c29e40e1ddc6cd507083917cb178e3b2ca42d Mon Sep 17 00:00:00 2001 From: jjstnlee Date: Sun, 7 Apr 2024 18:33:54 -0700 Subject: [PATCH 1/9] Implemented save story button --- src/app/(tabs)/home/index.tsx | 2 ++ src/components/ContentCard/ContentCard.tsx | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/app/(tabs)/home/index.tsx b/src/app/(tabs)/home/index.tsx index 81341d7..f45cf3d 100644 --- a/src/app/(tabs)/home/index.tsx +++ b/src/app/(tabs)/home/index.tsx @@ -132,6 +132,7 @@ function HomeScreen() { title={story.title} author={story.author_name} authorImage={story.author_image} + storyId={story.id} pressFunction={() => router.push({ pathname: '/story', @@ -161,6 +162,7 @@ function HomeScreen() { title={story.title} author={story.author_name} authorImage={story.author_image} + storyId={story.id} pressFunction={() => router.push({ pathname: '/story', diff --git a/src/components/ContentCard/ContentCard.tsx b/src/components/ContentCard/ContentCard.tsx index 4301cb9..145769a 100644 --- a/src/components/ContentCard/ContentCard.tsx +++ b/src/components/ContentCard/ContentCard.tsx @@ -8,13 +8,16 @@ import { } from 'react-native'; import styles from './styles'; +import { addUserStoryToReadingList } from '../../queries/savedStories'; import globalStyles from '../../styles/globalStyles'; +import { useSession } from '../../utils/AuthContext'; type ContentCardProps = { title: string; author: string; image: string; authorImage: string; + storyId: number; pressFunction: (event: GestureResponderEvent) => void; }; @@ -23,10 +26,13 @@ function ContentCard({ author, image, authorImage, + storyId, pressFunction, }: ContentCardProps) { + const { user } = useSession(); + const saveStory = () => { - console.log("testing '+' icon does something for story " + title); + addUserStoryToReadingList(user?.id, storyId); }; return ( @@ -60,7 +66,7 @@ function ContentCard({ null} + onPress={() => saveStory()} style={{ flexDirection: 'row' }} > Date: Sun, 14 Apr 2024 12:47:36 -0700 Subject: [PATCH 2/9] Move story image to assets --- .../save_story.png | Bin assets/saved_story.png | Bin 0 -> 12911 bytes src/components/ContentCard/ContentCard.tsx | 2 +- src/components/PreviewCard/PreviewCard.tsx | 2 +- src/components/PreviewCard/savedStoriesIcon.png | Bin 1052 -> 0 bytes 5 files changed, 2 insertions(+), 2 deletions(-) rename src/components/ContentCard/savedStoriesIcon.png => assets/save_story.png (100%) create mode 100644 assets/saved_story.png delete mode 100644 src/components/PreviewCard/savedStoriesIcon.png diff --git a/src/components/ContentCard/savedStoriesIcon.png b/assets/save_story.png similarity index 100% rename from src/components/ContentCard/savedStoriesIcon.png rename to assets/save_story.png diff --git a/assets/saved_story.png b/assets/saved_story.png new file mode 100644 index 0000000000000000000000000000000000000000..46c3e7b053fe8b9c8b2a5a15994de304b40a3767 GIT binary patch literal 12911 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 JufUi@{VzM@4g>%I literal 0 HcmV?d00001 diff --git a/src/components/ContentCard/ContentCard.tsx b/src/components/ContentCard/ContentCard.tsx index 2305563..b164b8c 100644 --- a/src/components/ContentCard/ContentCard.tsx +++ b/src/components/ContentCard/ContentCard.tsx @@ -85,7 +85,7 @@ function ContentCard({ saveStory()}> diff --git a/src/components/PreviewCard/PreviewCard.tsx b/src/components/PreviewCard/PreviewCard.tsx index dfe7322..45a31a7 100644 --- a/src/components/PreviewCard/PreviewCard.tsx +++ b/src/components/PreviewCard/PreviewCard.tsx @@ -48,7 +48,7 @@ function PreviewCard({ saveStory()}> diff --git a/src/components/PreviewCard/savedStoriesIcon.png b/src/components/PreviewCard/savedStoriesIcon.png deleted file mode 100644 index 65b2ea6e4dd47e1c590ef1cbe5d7afc29d8fcca7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 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: Mon, 15 Apr 2024 17:37:33 -0700 Subject: [PATCH 3/9] Add save story to preview card, one home screen viewing bug --- src/app/(tabs)/author/index.tsx | 1 + src/app/(tabs)/genre/index.tsx | 1 + src/app/(tabs)/home/index.tsx | 1 + src/app/(tabs)/search/index.tsx | 4 +- src/components/ContentCard/ContentCard.tsx | 22 ++++++++-- src/components/PreviewCard/PreviewCard.tsx | 26 +++++++++++- src/queries/reactions.tsx | 2 +- src/queries/savedStories.tsx | 48 ++++++++++++++++++---- 8 files changed, 91 insertions(+), 14 deletions(-) diff --git a/src/app/(tabs)/author/index.tsx b/src/app/(tabs)/author/index.tsx index 38b8b88..7767637 100644 --- a/src/app/(tabs)/author/index.tsx +++ b/src/app/(tabs)/author/index.tsx @@ -122,6 +122,7 @@ function AuthorScreen() { {authorStoryPreview?.map(story => ( ( ( ( ( void; }; +const saveStoryImage = require('../../../assets/save_story.png'); +const savedStoryImage = require('../../../assets/saved_story.png'); + function ContentCard({ title, author, @@ -31,9 +36,20 @@ function ContentCard({ pressFunction, }: ContentCardProps) { const { user } = useSession(); + const [storyIsSaved, setStoryIsSaved] = useState(false); + + useEffect(() => { + isStoryInReadingList(storyId, user?.id).then(storyInReadingList => setStoryIsSaved(storyInReadingList)) + }, [storyId]) + const saveStory = () => { - addUserStoryToReadingList(user?.id, storyId); + if (storyIsSaved) { + deleteUserStoryToReadingList(user?.id, storyId); + } else { + addUserStoryToReadingList(user?.id, storyId); + } + setStoryIsSaved(!storyIsSaved); }; return ( @@ -85,7 +101,7 @@ function ContentCard({ saveStory()}> diff --git a/src/components/PreviewCard/PreviewCard.tsx b/src/components/PreviewCard/PreviewCard.tsx index 45a31a7..db5b283 100644 --- a/src/components/PreviewCard/PreviewCard.tsx +++ b/src/components/PreviewCard/PreviewCard.tsx @@ -11,13 +11,20 @@ import Emoji from 'react-native-emoji'; import styles from './styles'; import globalStyles from '../../styles/globalStyles'; +import { useEffect, useState } from 'react'; +import { addUserStoryToReadingList, deleteUserStoryToReadingList, isStoryInReadingList } from '../../queries/savedStories'; +import { useSession } from '../../utils/AuthContext'; +import { useIsFocused } from '@react-navigation/native'; const placeholderImage = 'https://gwn-uploads.s3.amazonaws.com/wp-content/uploads/2021/10/10120952/Girls-Write-Now-logo-avatar.png'; +const saveStoryImage = require('../../../assets/save_story.png'); +const savedStoryImage = require('../../../assets/saved_story.png'); type PreviewCardProps = { title: string; image: string; + storyId: number; author: string; authorImage: string; excerpt: { html: string }; @@ -28,14 +35,29 @@ type PreviewCardProps = { function PreviewCard({ title, image, + storyId, author, authorImage, excerpt, tags, pressFunction, }: PreviewCardProps) { + const { user } = useSession(); + const isFocused = useIsFocused(); + const [storyIsSaved, setStoryIsSaved] = useState(false); + + useEffect(() => { + isStoryInReadingList(storyId, user?.id).then(storyInReadingList => setStoryIsSaved(storyInReadingList)) + }, [storyId, isFocused]) + + const saveStory = () => { - console.log("testing '+' icon does something for story " + title); + if (storyIsSaved) { + deleteUserStoryToReadingList(user?.id, storyId); + } else { + addUserStoryToReadingList(user?.id, storyId); + } + setStoryIsSaved(!storyIsSaved); }; return ( @@ -48,7 +70,7 @@ function PreviewCard({ saveStory()}> diff --git a/src/queries/reactions.tsx b/src/queries/reactions.tsx index 8a9da88..ffe68c4 100644 --- a/src/queries/reactions.tsx +++ b/src/queries/reactions.tsx @@ -53,7 +53,7 @@ export async function fetchAllReactionsToStory( `An error occured when trying to fetch reactions to a story', ${error}`, ); } else { - return data; + return data as Reactions[]; } } diff --git a/src/queries/savedStories.tsx b/src/queries/savedStories.tsx index 59f6eca..8b88460 100644 --- a/src/queries/savedStories.tsx +++ b/src/queries/savedStories.tsx @@ -1,7 +1,10 @@ import supabase from '../utils/supabase'; -const favorites = 'favorites'; -const readingList = 'reading list'; +enum SavedList { + FAVORITES = 'favorites', + READING_LIST = 'reading list' +} + async function fetchUserStories( user_id: string | undefined, @@ -48,11 +51,11 @@ async function fetchUserStories( } export async function fetchUserStoriesFavorites(user_id: string | undefined) { - return await fetchUserStories(user_id, favorites); + return await fetchUserStories(user_id, SavedList.FAVORITES); } export async function fetchUserStoriesReadingList(user_id: string | undefined) { - return await fetchUserStories(user_id, readingList); + return await fetchUserStories(user_id, SavedList.READING_LIST); } async function addUserStory( @@ -78,17 +81,32 @@ export async function addUserStoryToFavorites( user_id: string | undefined, story_id: number, ) { - addUserStory(user_id, story_id, favorites); + addUserStory(user_id, story_id, SavedList.FAVORITES); } export async function addUserStoryToReadingList( user_id: string | undefined, story_id: number, ) { - addUserStory(user_id, story_id, readingList); + addUserStory(user_id, story_id, SavedList.READING_LIST); +} + +export async function deleteUserStoryToFavorites( + user_id: string | undefined, + story_id: number, +) { + deleteUserStory(user_id, story_id, SavedList.FAVORITES); } -export async function deleteUserStories( +export async function deleteUserStoryToReadingList( + user_id: string | undefined, + story_id: number, +) { + deleteUserStory(user_id, story_id, SavedList.READING_LIST); +} + + +export async function deleteUserStory( user_id: string | undefined, story_id: number, name: string, @@ -108,3 +126,19 @@ export async function deleteUserStories( } } } + +export async function isStoryInReadingList(storyId: number, userId: string | undefined): Promise { + let { data, error } = await supabase + .rpc('is_story_saved_for_user', { + list_name: "reading list", + story_db_id: storyId, + user_uuid: userId, + }) + + if (error) { + console.error(error) + return false; + } + + return data; +} From 47b3c11533e7b944adbce3b0f8a1d46bbb6bdf2c Mon Sep 17 00:00:00 2001 From: Aditya Pawar Date: Wed, 17 Apr 2024 17:39:35 -0700 Subject: [PATCH 4/9] Add pubsub to sync preview and content cards --- package-lock.json | 9 +++ package.json | 3 +- src/app/_layout.tsx | 13 ++-- src/components/AuthorCard/AuthorCard.tsx | 4 +- src/components/ContentCard/ContentCard.tsx | 63 +++++++++++++++----- src/components/PreviewCard/PreviewCard.tsx | 69 +++++++++++++++++----- src/components/PreviewCard/styles.ts | 2 + src/queries/savedStories.tsx | 26 ++++---- src/utils/AuthContext.tsx | 12 ++-- src/utils/PubSubContext.tsx | 60 +++++++++++++++++++ 10 files changed, 202 insertions(+), 59 deletions(-) create mode 100644 src/utils/PubSubContext.tsx diff --git a/package-lock.json b/package-lock.json index f83fb7d..6829847 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,7 @@ "expo": "~49.0.11", "expo-constants": "~14.4.2", "expo-font": "~11.4.0", + "expo-image": "~1.3.5", "expo-linking": "~5.0.2", "expo-router": "^2.0.0", "expo-status-bar": "~1.6.0", @@ -11174,6 +11175,14 @@ "react-native": "*" } }, + "node_modules/expo-image": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/expo-image/-/expo-image-1.3.5.tgz", + "integrity": "sha512-yrIR2mnfIKbKcguoqWK3U5m3zvLPnonvSCabB2ErVGhws8zQs7ILYf+7T08j8U6eFcohjw0CoAFJ6RWNsX2EhA==", + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-keep-awake": { "version": "12.3.0", "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-12.3.0.tgz", diff --git a/package.json b/package.json index ed0b39d..d15d3b5 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,8 @@ "react-native-vector-icons": "^10.0.2", "react-scroll-to-top": "^3.0.0", "use-debounce": "^10.0.0", - "validator": "^13.11.0" + "validator": "^13.11.0", + "expo-image": "~1.3.5" }, "devDependencies": { "@babel/core": "^7.20.0", diff --git a/src/app/_layout.tsx b/src/app/_layout.tsx index c15ba80..b8f0527 100644 --- a/src/app/_layout.tsx +++ b/src/app/_layout.tsx @@ -2,6 +2,7 @@ import { Stack } from 'expo-router'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { AuthContextProvider } from '../utils/AuthContext'; +import { BooleanPubSubProvider } from '../utils/PubSubContext'; import ToastComponent from '../components/Toast/Toast'; import { Keyboard, TouchableWithoutFeedback } from 'react-native'; @@ -9,11 +10,13 @@ function StackLayout() { return ( - - - - - + + + + + + + diff --git a/src/components/AuthorCard/AuthorCard.tsx b/src/components/AuthorCard/AuthorCard.tsx index afdd2b5..99694ab 100644 --- a/src/components/AuthorCard/AuthorCard.tsx +++ b/src/components/AuthorCard/AuthorCard.tsx @@ -1,7 +1,5 @@ -import { Image, Pressable, Text, View } from 'react-native'; - +import { Image, Text, View } from 'react-native'; import styles from './styles'; -import globalStyles from '../../styles/globalStyles'; type AuthorCardProps = { name: string; diff --git a/src/components/ContentCard/ContentCard.tsx b/src/components/ContentCard/ContentCard.tsx index a6e32b6..034ff6f 100644 --- a/src/components/ContentCard/ContentCard.tsx +++ b/src/components/ContentCard/ContentCard.tsx @@ -1,19 +1,23 @@ import { GestureResponderEvent, - Image, Pressable, Text, View, TouchableOpacity, } from 'react-native'; +import { Image } from 'expo-image'; import styles from './styles'; -import { addUserStoryToReadingList, deleteUserStoryToReadingList, isStoryInReadingList } from '../../queries/savedStories'; +import { + addUserStoryToReadingList, + deleteUserStoryToReadingList, + isStoryInReadingList, +} from '../../queries/savedStories'; import globalStyles from '../../styles/globalStyles'; import { useSession } from '../../utils/AuthContext'; import Emoji from 'react-native-emoji'; -import { useEffect, useMemo, useState } from 'react'; - +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { usePubSub } from '../../utils/PubSubContext'; type ContentCardProps = { title: string; @@ -37,19 +41,47 @@ function ContentCard({ }: ContentCardProps) { const { user } = useSession(); const [storyIsSaved, setStoryIsSaved] = useState(false); + const { channels, initializeChannel, publish } = usePubSub(); + + const savedStoryImageComponent = useMemo(() => { + return ( + + ) + }, []) + const saveStoryImageComponent = useMemo(() => { + return ( + + ) + }, []) useEffect(() => { - isStoryInReadingList(storyId, user?.id).then(storyInReadingList => setStoryIsSaved(storyInReadingList)) - }, [storyId]) + isStoryInReadingList(storyId, user?.id).then(storyInReadingList => { + setStoryIsSaved(storyInReadingList) + initializeChannel(storyId); + }); + }, [storyId]); + useEffect(() => { + // if another card updates this story, update it here also + if (typeof channels[storyId] !== "undefined") { + setStoryIsSaved(channels[storyId]); + } + }, [channels[storyId]]); - const saveStory = () => { - if (storyIsSaved) { - deleteUserStoryToReadingList(user?.id, storyId); + const saveStory = async (saved: boolean) => { + setStoryIsSaved(saved); + publish(storyId, saved); + if (saved) { + await addUserStoryToReadingList(user?.id, storyId); } else { - addUserStoryToReadingList(user?.id, storyId); + await deleteUserStoryToReadingList(user?.id, storyId); } - setStoryIsSaved(!storyIsSaved); }; return ( @@ -98,11 +130,10 @@ function ContentCard({ - saveStory()}> - + saveStory(!storyIsSaved)}> + {storyIsSaved ? + savedStoryImageComponent : saveStoryImageComponent + } diff --git a/src/components/PreviewCard/PreviewCard.tsx b/src/components/PreviewCard/PreviewCard.tsx index db5b283..fcb7b0b 100644 --- a/src/components/PreviewCard/PreviewCard.tsx +++ b/src/components/PreviewCard/PreviewCard.tsx @@ -1,20 +1,25 @@ import * as cheerio from 'cheerio'; import { GestureResponderEvent, - Image, Pressable, Text, TouchableOpacity, View, } from 'react-native'; import Emoji from 'react-native-emoji'; +import { Image } from 'expo-image'; import styles from './styles'; import globalStyles from '../../styles/globalStyles'; -import { useEffect, useState } from 'react'; -import { addUserStoryToReadingList, deleteUserStoryToReadingList, isStoryInReadingList } from '../../queries/savedStories'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { + addUserStoryToReadingList, + deleteUserStoryToReadingList, + isStoryInReadingList, +} from '../../queries/savedStories'; import { useSession } from '../../utils/AuthContext'; import { useIsFocused } from '@react-navigation/native'; +import { usePubSub } from '../../utils/PubSubContext'; const placeholderImage = 'https://gwn-uploads.s3.amazonaws.com/wp-content/uploads/2021/10/10120952/Girls-Write-Now-logo-avatar.png'; @@ -45,19 +50,54 @@ function PreviewCard({ const { user } = useSession(); const isFocused = useIsFocused(); const [storyIsSaved, setStoryIsSaved] = useState(false); + const { channels, initializeChannel, publish } = usePubSub(); + + const savedStoryImageComponent = useMemo(() => { + return ( + + ) + }, []) + const saveStoryImageComponent = useMemo(() => { + return ( + + ) + }, []) useEffect(() => { - isStoryInReadingList(storyId, user?.id).then(storyInReadingList => setStoryIsSaved(storyInReadingList)) - }, [storyId, isFocused]) + isStoryInReadingList(storyId, user?.id).then(storyInReadingList => { + setStoryIsSaved(storyInReadingList) + initializeChannel(storyId); + }); + }, [storyId]); + useEffect(() => { + // if another card updates this story, update it here also + if (typeof channels[storyId] !== "undefined") { + setStoryIsSaved(channels[storyId]) + } + }, [channels[storyId]]); + + + useEffect(() => { + isStoryInReadingList(storyId, user?.id).then(storyInReadingList => + setStoryIsSaved(storyInReadingList), + ); + }, [storyId, isFocused]); - const saveStory = () => { - if (storyIsSaved) { - deleteUserStoryToReadingList(user?.id, storyId); + const saveStory = async (saved: boolean) => { + setStoryIsSaved(saved); + publish(storyId, saved); + if (saved) { + await addUserStoryToReadingList(user?.id, storyId); } else { - addUserStoryToReadingList(user?.id, storyId); + await deleteUserStoryToReadingList(user?.id, storyId); } - setStoryIsSaved(!storyIsSaved); }; return ( @@ -67,11 +107,10 @@ function PreviewCard({ {title} - saveStory()}> - + saveStory(!storyIsSaved)}> + {storyIsSaved ? + savedStoryImageComponent : saveStoryImageComponent + } diff --git a/src/components/PreviewCard/styles.ts b/src/components/PreviewCard/styles.ts index ed355c7..d8d3ef1 100644 --- a/src/components/PreviewCard/styles.ts +++ b/src/components/PreviewCard/styles.ts @@ -50,9 +50,11 @@ const styles = StyleSheet.create({ paddingTop: 16, paddingLeft: 12, paddingRight: 12, + paddingBottom: 8, borderBottomColor: '#EBEBEB', borderBottomWidth: StyleSheet.hairlineWidth, flexDirection: 'row', + flexGrow: 1, justifyContent: 'space-between', }, tag: { diff --git a/src/queries/savedStories.tsx b/src/queries/savedStories.tsx index 8b88460..78c4e53 100644 --- a/src/queries/savedStories.tsx +++ b/src/queries/savedStories.tsx @@ -2,10 +2,9 @@ import supabase from '../utils/supabase'; enum SavedList { FAVORITES = 'favorites', - READING_LIST = 'reading list' + READING_LIST = 'reading list', } - async function fetchUserStories( user_id: string | undefined, name: string | undefined, @@ -65,13 +64,13 @@ async function addUserStory( ) { const { error } = await supabase .from('saved_stories') - .insert([{ user_id: user_id, story_id: story_id, name: name }]) + .upsert([{ user_id: user_id, story_id: story_id, name: name }]) .select(); if (error) { if (process.env.NODE_ENV !== 'production') { throw new Error( - `An error occured when trying to set user saved stories: ${error.details}`, + `An error occured when trying to set user saved stories: ${JSON.stringify(error)}`, ); } } @@ -105,7 +104,6 @@ export async function deleteUserStoryToReadingList( deleteUserStory(user_id, story_id, SavedList.READING_LIST); } - export async function deleteUserStory( user_id: string | undefined, story_id: number, @@ -127,16 +125,18 @@ export async function deleteUserStory( } } -export async function isStoryInReadingList(storyId: number, userId: string | undefined): Promise { - let { data, error } = await supabase - .rpc('is_story_saved_for_user', { - list_name: "reading list", - story_db_id: storyId, - user_uuid: userId, - }) +export async function isStoryInReadingList( + storyId: number, + userId: string | undefined, +): Promise { + let { data, error } = await supabase.rpc('is_story_saved_for_user', { + list_name: 'reading list', + story_db_id: storyId, + user_uuid: userId, + }); if (error) { - console.error(error) + console.error(error); return false; } diff --git a/src/utils/AuthContext.tsx b/src/utils/AuthContext.tsx index 4273a1c..34d711d 100644 --- a/src/utils/AuthContext.tsx +++ b/src/utils/AuthContext.tsx @@ -31,13 +31,13 @@ export interface AuthState { resendVerification: (email: string) => Promise; resetPassword: (email: string) => Promise< | { - data: object; - error: null; - } + data: object; + error: null; + } | { - data: null; - error: AuthError; - } + data: null; + error: AuthError; + } >; updateUser: (attributes: UserAttributes) => Promise; signOut: () => Promise; diff --git a/src/utils/PubSubContext.tsx b/src/utils/PubSubContext.tsx new file mode 100644 index 0000000..5bfe68a --- /dev/null +++ b/src/utils/PubSubContext.tsx @@ -0,0 +1,60 @@ +import React, { + createContext, + useContext, + useMemo, + useState, +} from 'react'; + +export interface PubSubState { + channels: Record; + initializeChannel: (id: number) => void; + publish: (id: number, message: boolean) => void; +} + +const BooleanPubSubContext = createContext({} as PubSubState); + +export function usePubSub() { + const value = useContext(BooleanPubSubContext); + if (process.env.NODE_ENV !== 'production') { + if (!value) { + throw new Error( + 'usePubSub must be wrapped in a ', + ); + } + } + + return value; +} + +export function BooleanPubSubProvider({ + children, +}: { + children: React.ReactNode; +}) { + const [channels, setChannels] = useState>({}) + + const initializeChannel = (id: number) => { + if (!(id in channels)) { + setChannels({ ...channels, [id]: undefined }) + } + } + + const publish = (id: number, message: boolean) => { + setChannels({ ...channels, [id]: message }) + } + + const authContextValue = useMemo( + () => ({ + channels, + initializeChannel, + publish, + }), + [channels], + ); + + return ( + + {children} + + ); +} From ce4d017e1df6ccd909aff67ec599797ba0bb6cf7 Mon Sep 17 00:00:00 2001 From: Aditya Pawar Date: Wed, 17 Apr 2024 17:42:13 -0700 Subject: [PATCH 5/9] Bug fixes --- src/components/ContentCard/ContentCard.tsx | 28 +++++++-------------- src/components/PreviewCard/PreviewCard.tsx | 29 ++++++---------------- src/queries/savedStories.tsx | 4 ++- src/utils/AuthContext.tsx | 12 ++++----- src/utils/PubSubContext.tsx | 19 ++++++-------- 5 files changed, 34 insertions(+), 58 deletions(-) diff --git a/src/components/ContentCard/ContentCard.tsx b/src/components/ContentCard/ContentCard.tsx index 034ff6f..d093018 100644 --- a/src/components/ContentCard/ContentCard.tsx +++ b/src/components/ContentCard/ContentCard.tsx @@ -44,32 +44,22 @@ function ContentCard({ const { channels, initializeChannel, publish } = usePubSub(); const savedStoryImageComponent = useMemo(() => { - return ( - - ) - }, []) + return ; + }, []); const saveStoryImageComponent = useMemo(() => { - return ( - - ) - }, []) + return ; + }, []); useEffect(() => { isStoryInReadingList(storyId, user?.id).then(storyInReadingList => { - setStoryIsSaved(storyInReadingList) + setStoryIsSaved(storyInReadingList); initializeChannel(storyId); }); }, [storyId]); useEffect(() => { // if another card updates this story, update it here also - if (typeof channels[storyId] !== "undefined") { + if (typeof channels[storyId] !== 'undefined') { setStoryIsSaved(channels[storyId]); } }, [channels[storyId]]); @@ -131,9 +121,9 @@ function ContentCard({ saveStory(!storyIsSaved)}> - {storyIsSaved ? - savedStoryImageComponent : saveStoryImageComponent - } + {storyIsSaved + ? savedStoryImageComponent + : saveStoryImageComponent} diff --git a/src/components/PreviewCard/PreviewCard.tsx b/src/components/PreviewCard/PreviewCard.tsx index fcb7b0b..d07304d 100644 --- a/src/components/PreviewCard/PreviewCard.tsx +++ b/src/components/PreviewCard/PreviewCard.tsx @@ -53,37 +53,26 @@ function PreviewCard({ const { channels, initializeChannel, publish } = usePubSub(); const savedStoryImageComponent = useMemo(() => { - return ( - - ) - }, []) + return ; + }, []); const saveStoryImageComponent = useMemo(() => { - return ( - - ) - }, []) + return ; + }, []); useEffect(() => { isStoryInReadingList(storyId, user?.id).then(storyInReadingList => { - setStoryIsSaved(storyInReadingList) + setStoryIsSaved(storyInReadingList); initializeChannel(storyId); }); }, [storyId]); useEffect(() => { // if another card updates this story, update it here also - if (typeof channels[storyId] !== "undefined") { - setStoryIsSaved(channels[storyId]) + if (typeof channels[storyId] !== 'undefined') { + setStoryIsSaved(channels[storyId]); } }, [channels[storyId]]); - useEffect(() => { isStoryInReadingList(storyId, user?.id).then(storyInReadingList => setStoryIsSaved(storyInReadingList), @@ -108,9 +97,7 @@ function PreviewCard({ {title} saveStory(!storyIsSaved)}> - {storyIsSaved ? - savedStoryImageComponent : saveStoryImageComponent - } + {storyIsSaved ? savedStoryImageComponent : saveStoryImageComponent} diff --git a/src/queries/savedStories.tsx b/src/queries/savedStories.tsx index 78c4e53..0416ecb 100644 --- a/src/queries/savedStories.tsx +++ b/src/queries/savedStories.tsx @@ -70,7 +70,9 @@ async function addUserStory( if (error) { if (process.env.NODE_ENV !== 'production') { throw new Error( - `An error occured when trying to set user saved stories: ${JSON.stringify(error)}`, + `An error occured when trying to set user saved stories: ${JSON.stringify( + error, + )}`, ); } } diff --git a/src/utils/AuthContext.tsx b/src/utils/AuthContext.tsx index 34d711d..4273a1c 100644 --- a/src/utils/AuthContext.tsx +++ b/src/utils/AuthContext.tsx @@ -31,13 +31,13 @@ export interface AuthState { resendVerification: (email: string) => Promise; resetPassword: (email: string) => Promise< | { - data: object; - error: null; - } + data: object; + error: null; + } | { - data: null; - error: AuthError; - } + data: null; + error: AuthError; + } >; updateUser: (attributes: UserAttributes) => Promise; signOut: () => Promise; diff --git a/src/utils/PubSubContext.tsx b/src/utils/PubSubContext.tsx index 5bfe68a..ffc4c1d 100644 --- a/src/utils/PubSubContext.tsx +++ b/src/utils/PubSubContext.tsx @@ -1,9 +1,4 @@ -import React, { - createContext, - useContext, - useMemo, - useState, -} from 'react'; +import React, { createContext, useContext, useMemo, useState } from 'react'; export interface PubSubState { channels: Record; @@ -31,17 +26,19 @@ export function BooleanPubSubProvider({ }: { children: React.ReactNode; }) { - const [channels, setChannels] = useState>({}) + const [channels, setChannels] = useState>( + {}, + ); const initializeChannel = (id: number) => { if (!(id in channels)) { - setChannels({ ...channels, [id]: undefined }) + setChannels({ ...channels, [id]: undefined }); } - } + }; const publish = (id: number, message: boolean) => { - setChannels({ ...channels, [id]: message }) - } + setChannels({ ...channels, [id]: message }); + }; const authContextValue = useMemo( () => ({ From 5314e983d0eef36a515c746cd5c16acfe2b2facb Mon Sep 17 00:00:00 2001 From: Aditya Pawar Date: Wed, 17 Apr 2024 17:49:22 -0700 Subject: [PATCH 6/9] Refactor --- src/components/ContentCard/ContentCard.tsx | 21 +++++++++++---------- src/components/PreviewCard/PreviewCard.tsx | 22 +++++++++++++--------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/components/ContentCard/ContentCard.tsx b/src/components/ContentCard/ContentCard.tsx index d093018..f297d08 100644 --- a/src/components/ContentCard/ContentCard.tsx +++ b/src/components/ContentCard/ContentCard.tsx @@ -43,13 +43,6 @@ function ContentCard({ const [storyIsSaved, setStoryIsSaved] = useState(false); const { channels, initializeChannel, publish } = usePubSub(); - const savedStoryImageComponent = useMemo(() => { - return ; - }, []); - const saveStoryImageComponent = useMemo(() => { - return ; - }, []); - useEffect(() => { isStoryInReadingList(storyId, user?.id).then(storyInReadingList => { setStoryIsSaved(storyInReadingList); @@ -121,9 +114,17 @@ function ContentCard({ saveStory(!storyIsSaved)}> - {storyIsSaved - ? savedStoryImageComponent - : saveStoryImageComponent} + {storyIsSaved ? ( + + ) : ( + + )} diff --git a/src/components/PreviewCard/PreviewCard.tsx b/src/components/PreviewCard/PreviewCard.tsx index d07304d..708ed80 100644 --- a/src/components/PreviewCard/PreviewCard.tsx +++ b/src/components/PreviewCard/PreviewCard.tsx @@ -52,13 +52,6 @@ function PreviewCard({ const [storyIsSaved, setStoryIsSaved] = useState(false); const { channels, initializeChannel, publish } = usePubSub(); - const savedStoryImageComponent = useMemo(() => { - return ; - }, []); - const saveStoryImageComponent = useMemo(() => { - return ; - }, []); - useEffect(() => { isStoryInReadingList(storyId, user?.id).then(storyInReadingList => { setStoryIsSaved(storyInReadingList); @@ -81,7 +74,8 @@ function PreviewCard({ const saveStory = async (saved: boolean) => { setStoryIsSaved(saved); - publish(storyId, saved); + publish(storyId, saved); // update other cards with this story + if (saved) { await addUserStoryToReadingList(user?.id, storyId); } else { @@ -97,7 +91,17 @@ function PreviewCard({ {title} saveStory(!storyIsSaved)}> - {storyIsSaved ? savedStoryImageComponent : saveStoryImageComponent} + {storyIsSaved ? ( + + ) : ( + + )} From 81f3b38cfaff31d24a75d6a4053fd368a51f050a Mon Sep 17 00:00:00 2001 From: Aditya Pawar Date: Wed, 17 Apr 2024 18:01:11 -0700 Subject: [PATCH 7/9] Create SavedStoryButton componenet --- src/components/ContentCard/ContentCard.tsx | 46 +------------ src/components/PreviewCard/PreviewCard.tsx | 53 ++------------- .../SaveStoryButton/SaveStoryButton.tsx | 64 +++++++++++++++++++ 3 files changed, 71 insertions(+), 92 deletions(-) create mode 100644 src/components/SaveStoryButton/SaveStoryButton.tsx diff --git a/src/components/ContentCard/ContentCard.tsx b/src/components/ContentCard/ContentCard.tsx index f297d08..3a6c9d5 100644 --- a/src/components/ContentCard/ContentCard.tsx +++ b/src/components/ContentCard/ContentCard.tsx @@ -18,6 +18,7 @@ import { useSession } from '../../utils/AuthContext'; import Emoji from 'react-native-emoji'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { usePubSub } from '../../utils/PubSubContext'; +import SaveStoryButton from '../SaveStoryButton/SaveStoryButton'; type ContentCardProps = { title: string; @@ -28,9 +29,6 @@ type ContentCardProps = { pressFunction: (event: GestureResponderEvent) => void; }; -const saveStoryImage = require('../../../assets/save_story.png'); -const savedStoryImage = require('../../../assets/saved_story.png'); - function ContentCard({ title, author, @@ -39,34 +37,6 @@ function ContentCard({ storyId, pressFunction, }: ContentCardProps) { - const { user } = useSession(); - const [storyIsSaved, setStoryIsSaved] = useState(false); - const { channels, initializeChannel, publish } = usePubSub(); - - useEffect(() => { - isStoryInReadingList(storyId, user?.id).then(storyInReadingList => { - setStoryIsSaved(storyInReadingList); - initializeChannel(storyId); - }); - }, [storyId]); - - useEffect(() => { - // if another card updates this story, update it here also - if (typeof channels[storyId] !== 'undefined') { - setStoryIsSaved(channels[storyId]); - } - }, [channels[storyId]]); - - const saveStory = async (saved: boolean) => { - setStoryIsSaved(saved); - publish(storyId, saved); - if (saved) { - await addUserStoryToReadingList(user?.id, storyId); - } else { - await deleteUserStoryToReadingList(user?.id, storyId); - } - }; - return ( @@ -113,18 +83,8 @@ function ContentCard({ - saveStory(!storyIsSaved)}> - {storyIsSaved ? ( - - ) : ( - - )} + + diff --git a/src/components/PreviewCard/PreviewCard.tsx b/src/components/PreviewCard/PreviewCard.tsx index 708ed80..8ba1a21 100644 --- a/src/components/PreviewCard/PreviewCard.tsx +++ b/src/components/PreviewCard/PreviewCard.tsx @@ -11,7 +11,7 @@ import { Image } from 'expo-image'; import styles from './styles'; import globalStyles from '../../styles/globalStyles'; -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useEffect, useState } from 'react'; import { addUserStoryToReadingList, deleteUserStoryToReadingList, @@ -20,6 +20,7 @@ import { import { useSession } from '../../utils/AuthContext'; import { useIsFocused } from '@react-navigation/native'; import { usePubSub } from '../../utils/PubSubContext'; +import SaveStoryButton from '../SaveStoryButton/SaveStoryButton'; const placeholderImage = 'https://gwn-uploads.s3.amazonaws.com/wp-content/uploads/2021/10/10120952/Girls-Write-Now-logo-avatar.png'; @@ -47,42 +48,6 @@ function PreviewCard({ tags, pressFunction, }: PreviewCardProps) { - const { user } = useSession(); - const isFocused = useIsFocused(); - const [storyIsSaved, setStoryIsSaved] = useState(false); - const { channels, initializeChannel, publish } = usePubSub(); - - useEffect(() => { - isStoryInReadingList(storyId, user?.id).then(storyInReadingList => { - setStoryIsSaved(storyInReadingList); - initializeChannel(storyId); - }); - }, [storyId]); - - useEffect(() => { - // if another card updates this story, update it here also - if (typeof channels[storyId] !== 'undefined') { - setStoryIsSaved(channels[storyId]); - } - }, [channels[storyId]]); - - useEffect(() => { - isStoryInReadingList(storyId, user?.id).then(storyInReadingList => - setStoryIsSaved(storyInReadingList), - ); - }, [storyId, isFocused]); - - const saveStory = async (saved: boolean) => { - setStoryIsSaved(saved); - publish(storyId, saved); // update other cards with this story - - if (saved) { - await addUserStoryToReadingList(user?.id, storyId); - } else { - await deleteUserStoryToReadingList(user?.id, storyId); - } - }; - return ( @@ -90,18 +55,8 @@ function PreviewCard({ {title} - saveStory(!storyIsSaved)}> - {storyIsSaved ? ( - - ) : ( - - )} + + diff --git a/src/components/SaveStoryButton/SaveStoryButton.tsx b/src/components/SaveStoryButton/SaveStoryButton.tsx new file mode 100644 index 0000000..9aded23 --- /dev/null +++ b/src/components/SaveStoryButton/SaveStoryButton.tsx @@ -0,0 +1,64 @@ +import { useEffect, useState } from 'react'; +import { + addUserStoryToReadingList, + deleteUserStoryToReadingList, + isStoryInReadingList, +} from '../../queries/savedStories'; +import { usePubSub } from '../../utils/PubSubContext'; +import { useSession } from '../../utils/AuthContext'; +import { Image } from 'expo-image'; +import { TouchableOpacity } from 'react-native-gesture-handler'; + +type SaveStoryButtonProps = { + storyId: number; +}; + +const saveStoryImage = require('../../../assets/save_story.png'); +const savedStoryImage = require('../../../assets/saved_story.png'); + +export default function SaveStoryButton({ storyId }: SaveStoryButtonProps) { + const { user } = useSession(); + const [storyIsSaved, setStoryIsSaved] = useState(false); + const { channels, initializeChannel, publish } = usePubSub(); + + useEffect(() => { + isStoryInReadingList(storyId, user?.id).then(storyInReadingList => { + setStoryIsSaved(storyInReadingList); + initializeChannel(storyId); + }); + }, [storyId]); + + useEffect(() => { + // if another card updates this story, update it here also + if (typeof channels[storyId] !== 'undefined') { + setStoryIsSaved(channels[storyId]); + } + }, [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 + + if (saved) { + await addUserStoryToReadingList(user?.id, storyId); + } else { + await deleteUserStoryToReadingList(user?.id, storyId); + } + }; + + return ( + saveStory(!storyIsSaved)}> + {storyIsSaved ? ( + + ) : ( + + )} + + ); +} From ca91cc4c3e15870e72da1d2176390c55180cd5b2 Mon Sep 17 00:00:00 2001 From: Aditya Pawar Date: Wed, 17 Apr 2024 18:02:35 -0700 Subject: [PATCH 8/9] Remove unused imports --- src/components/ContentCard/ContentCard.tsx | 8 -------- src/components/PreviewCard/PreviewCard.tsx | 11 ----------- 2 files changed, 19 deletions(-) diff --git a/src/components/ContentCard/ContentCard.tsx b/src/components/ContentCard/ContentCard.tsx index 3a6c9d5..c766751 100644 --- a/src/components/ContentCard/ContentCard.tsx +++ b/src/components/ContentCard/ContentCard.tsx @@ -8,16 +8,8 @@ import { import { Image } from 'expo-image'; import styles from './styles'; -import { - addUserStoryToReadingList, - deleteUserStoryToReadingList, - isStoryInReadingList, -} from '../../queries/savedStories'; import globalStyles from '../../styles/globalStyles'; -import { useSession } from '../../utils/AuthContext'; import Emoji from 'react-native-emoji'; -import { useCallback, useEffect, useMemo, useState } from 'react'; -import { usePubSub } from '../../utils/PubSubContext'; import SaveStoryButton from '../SaveStoryButton/SaveStoryButton'; type ContentCardProps = { diff --git a/src/components/PreviewCard/PreviewCard.tsx b/src/components/PreviewCard/PreviewCard.tsx index 8ba1a21..71d6d05 100644 --- a/src/components/PreviewCard/PreviewCard.tsx +++ b/src/components/PreviewCard/PreviewCard.tsx @@ -11,21 +11,10 @@ import { Image } from 'expo-image'; import styles from './styles'; import globalStyles from '../../styles/globalStyles'; -import { useEffect, useState } from 'react'; -import { - addUserStoryToReadingList, - deleteUserStoryToReadingList, - isStoryInReadingList, -} from '../../queries/savedStories'; -import { useSession } from '../../utils/AuthContext'; -import { useIsFocused } from '@react-navigation/native'; -import { usePubSub } from '../../utils/PubSubContext'; import SaveStoryButton from '../SaveStoryButton/SaveStoryButton'; const placeholderImage = 'https://gwn-uploads.s3.amazonaws.com/wp-content/uploads/2021/10/10120952/Girls-Write-Now-logo-avatar.png'; -const saveStoryImage = require('../../../assets/save_story.png'); -const savedStoryImage = require('../../../assets/saved_story.png'); type PreviewCardProps = { title: string; From fe978499781164d458543e4eb0177aaf5ff50823 Mon Sep 17 00:00:00 2001 From: Aditya Pawar Date: Wed, 17 Apr 2024 18:05:52 -0700 Subject: [PATCH 9/9] Fix type errors --- src/components/SaveStoryButton/SaveStoryButton.tsx | 2 +- src/utils/PubSubContext.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/SaveStoryButton/SaveStoryButton.tsx b/src/components/SaveStoryButton/SaveStoryButton.tsx index 9aded23..c4d4331 100644 --- a/src/components/SaveStoryButton/SaveStoryButton.tsx +++ b/src/components/SaveStoryButton/SaveStoryButton.tsx @@ -31,7 +31,7 @@ export default function SaveStoryButton({ storyId }: SaveStoryButtonProps) { useEffect(() => { // if another card updates this story, update it here also if (typeof channels[storyId] !== 'undefined') { - setStoryIsSaved(channels[storyId]); + setStoryIsSaved(channels[storyId] ?? false); } }, [channels[storyId]]); diff --git a/src/utils/PubSubContext.tsx b/src/utils/PubSubContext.tsx index ffc4c1d..fbb9ada 100644 --- a/src/utils/PubSubContext.tsx +++ b/src/utils/PubSubContext.tsx @@ -1,7 +1,7 @@ import React, { createContext, useContext, useMemo, useState } from 'react'; export interface PubSubState { - channels: Record; + channels: Record; initializeChannel: (id: number) => void; publish: (id: number, message: boolean) => void; }