diff --git a/app.json b/app.json
index 6a1cdecc..a874bffd 100644
--- a/app.json
+++ b/app.json
@@ -17,16 +17,24 @@
"supportsTablet": true
},
"android": {
+ "package": "org.calblueprint.girlswritenow",
"softwareKeyboardLayoutMode": "pan",
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"
- },
- "softwareKeyboardLayoutMode": "pan"
+ }
},
"web": {
"favicon": "./assets/favicon.png"
},
- "plugins": ["expo-router"]
+ "plugins": ["expo-router"],
+ "extra": {
+ "router": {
+ "origin": false
+ },
+ "eas": {
+ "projectId": "12e1580c-e57a-466e-bc0d-c7a051565998"
+ }
+ }
}
}
diff --git a/package-lock.json b/package-lock.json
index da49b463..4bd8250b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,6 +12,9 @@
"@emotion/styled": "^11.11.0",
"@expo-google-fonts/manrope": "^0.2.3",
"@expo/vector-icons": "^13.0.0",
+ "@fortawesome/fontawesome-svg-core": "^6.5.2",
+ "@fortawesome/free-solid-svg-icons": "^6.5.2",
+ "@fortawesome/react-native-fontawesome": "^0.3.0",
"@mui/icons-material": "^5.14.13",
"@mui/material": "^5.14.13",
"@mui/styled-engine-sc": "^6.0.0-alpha.1",
@@ -3656,6 +3659,53 @@
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz",
"integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A=="
},
+ "node_modules/@fortawesome/fontawesome-common-types": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.2.tgz",
+ "integrity": "sha512-gBxPg3aVO6J0kpfHNILc+NMhXnqHumFxOmjYCFfOiLZfwhnnfhtsdA2hfJlDnj+8PjAs6kKQPenOTKj3Rf7zHw==",
+ "hasInstallScript": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/fontawesome-svg-core": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.2.tgz",
+ "integrity": "sha512-5CdaCBGl8Rh9ohNdxeeTMxIj8oc3KNBgIeLMvJosBMdslK/UnEB8rzyDRrbKdL1kDweqBPo4GT9wvnakHWucZw==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.5.2"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/free-solid-svg-icons": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.2.tgz",
+ "integrity": "sha512-QWFZYXFE7O1Gr1dTIp+D6UcFUF0qElOnZptpi7PBUMylJh+vFmIedVe1Ir6RM1t2tEQLLSV1k7bR4o92M+uqlw==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.5.2"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/react-native-fontawesome": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@fortawesome/react-native-fontawesome/-/react-native-fontawesome-0.3.0.tgz",
+ "integrity": "sha512-wSfetdK4+b/pvPbM2v+bZ5hfNlwtk9l3QuJo59sbMrxJalfX7BuF2WsSIWMSxfWwSsbOtY4+TUs6uw/rE59NJA==",
+ "dependencies": {
+ "humps": "^2.0.1",
+ "prop-types": "^15.7.2"
+ },
+ "peerDependencies": {
+ "@fortawesome/fontawesome-svg-core": "~1 || ~6",
+ "react-native": ">= 0.67",
+ "react-native-svg": ">= 11.x"
+ }
+ },
"node_modules/@gar/promisify": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
@@ -12522,6 +12572,11 @@
"node": ">=10.17.0"
}
},
+ "node_modules/humps": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/humps/-/humps-2.0.1.tgz",
+ "integrity": "sha512-E0eIbrFWUhwfXJmsbdjRQFQPrl5pTEoKlz163j1mTqqUnU9PgR4AgB8AIITzuB3vLBdxZXyZ9TDIrwB2OASz4g=="
+ },
"node_modules/husky": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz",
diff --git a/package.json b/package.json
index 45a2c09d..d058a0c8 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,9 @@
"@emotion/styled": "^11.11.0",
"@expo-google-fonts/manrope": "^0.2.3",
"@expo/vector-icons": "^13.0.0",
+ "@fortawesome/fontawesome-svg-core": "^6.5.2",
+ "@fortawesome/free-solid-svg-icons": "^6.5.2",
+ "@fortawesome/react-native-fontawesome": "^0.3.0",
"@mui/icons-material": "^5.14.13",
"@mui/material": "^5.14.13",
"@mui/styled-engine-sc": "^6.0.0-alpha.1",
diff --git a/src/app/(tabs)/genre/_layout.tsx b/src/app/(tabs)/genre/_layout.tsx
index d021a461..d309c307 100644
--- a/src/app/(tabs)/genre/_layout.tsx
+++ b/src/app/(tabs)/genre/_layout.tsx
@@ -1,10 +1,14 @@
import { Stack } from 'expo-router';
+import { FilterContextProvider } from '../../../utils/FilterContext';
+
function StackLayout() {
return (
-
-
-
+
+
+
+
+
);
}
diff --git a/src/app/(tabs)/genre/index.tsx b/src/app/(tabs)/genre/index.tsx
index 04483ea9..5bf00f55 100644
--- a/src/app/(tabs)/genre/index.tsx
+++ b/src/app/(tabs)/genre/index.tsx
@@ -7,13 +7,14 @@ import {
Text,
FlatList,
} from 'react-native';
-import { MultiSelect } from 'react-native-element-dropdown';
+import { Dropdown, MultiSelect } from 'react-native-element-dropdown';
import { Icon } from 'react-native-elements';
import { TouchableOpacity } from 'react-native-gesture-handler';
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';
diff --git a/src/app/(tabs)/genre/styles.tsx b/src/app/(tabs)/genre/styles.tsx
index 8d69590b..181292de 100644
--- a/src/app/(tabs)/genre/styles.tsx
+++ b/src/app/(tabs)/genre/styles.tsx
@@ -1,6 +1,7 @@
import { StyleSheet } from 'react-native';
import colors from '../../../styles/colors';
+import globalStyles from '../../../styles/globalStyles';
const styles = StyleSheet.create({
textSelected: {
@@ -12,7 +13,6 @@ const styles = StyleSheet.create({
width: '100%',
flex: 1,
},
-
flatListStyle: {
paddingTop: 15,
},
diff --git a/src/app/(tabs)/home/index.tsx b/src/app/(tabs)/home/index.tsx
index 911f8a57..2c8c0e3e 100644
--- a/src/app/(tabs)/home/index.tsx
+++ b/src/app/(tabs)/home/index.tsx
@@ -7,6 +7,7 @@ import { SafeAreaView } from 'react-native-safe-area-context';
import styles from './styles';
import ContentCard from '../../../components/ContentCard/ContentCard';
import PreviewCard from '../../../components/PreviewCard/PreviewCard';
+
import { fetchUsername } from '../../../queries/profiles';
import {
fetchFeaturedStoriesDescription,
diff --git a/src/app/(tabs)/search/index.tsx b/src/app/(tabs)/search/index.tsx
index 6163689c..10496c53 100644
--- a/src/app/(tabs)/search/index.tsx
+++ b/src/app/(tabs)/search/index.tsx
@@ -230,14 +230,14 @@ function SearchScreen() {
}}
/>
- {search && (
-
-
- )}
+ {/* {search && ( */}
+ {/* */}
+ {/* */}
+ {/* )} */}
{showRecents &&
(search && searchResults.length > 0 ? (
@@ -424,11 +424,11 @@ function SearchScreen() {
/>
)}
-
+ {/* */}
);
diff --git a/src/app/(tabs)/story/index.tsx b/src/app/(tabs)/story/index.tsx
index 50602db8..33ada971 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 ReactionPicker from '../../../components/ReactionPicker/ReactionPicker';
import BackButton from '../../../components/BackButton/BackButton';
import { fetchStory } from '../../../queries/stories';
import { Story } from '../../../queries/types';
@@ -201,6 +202,9 @@ function StoryScreen() {
>
Back To Top
+
+
+
)}
diff --git a/src/app/(tabs)/story/styles.ts b/src/app/(tabs)/story/styles.ts
index 0a2dc686..9dd547c2 100644
--- a/src/app/(tabs)/story/styles.ts
+++ b/src/app/(tabs)/story/styles.ts
@@ -57,6 +57,17 @@ const styles = StyleSheet.create({
process: {
marginBottom: 16,
},
+ backToTopButtonText: {
+ fontFamily: 'Manrope-Regular',
+ fontSize: 12,
+ fontWeight: '800',
+ textAlign: 'left',
+ color: 'black',
+ },
+ bottomReactionContainer: {
+ flex: 1,
+ justifyContent: 'space-around',
+ },
});
export default styles;
diff --git a/src/components/ContentCard/ContentCard.tsx b/src/components/ContentCard/ContentCard.tsx
index b3c1d35f..181d0b9a 100644
--- a/src/components/ContentCard/ContentCard.tsx
+++ b/src/components/ContentCard/ContentCard.tsx
@@ -39,7 +39,7 @@ function ContentCard({
(async () => {
const temp = await fetchAllReactionsToStory(id);
if (temp != null) {
- setReactions(temp.map(r => r.reaction));
+ setReactions(temp);
return;
}
@@ -76,8 +76,8 @@ function ContentCard({
-
-
+
+
diff --git a/src/components/FilterModal/ChildFilter.tsx b/src/components/FilterModal/ChildFilter.tsx
new file mode 100644
index 00000000..3b93abd1
--- /dev/null
+++ b/src/components/FilterModal/ChildFilter.tsx
@@ -0,0 +1,27 @@
+import { CheckBox } from '@rneui/themed';
+import { memo } from 'react';
+
+type ChildFilterProps = {
+ id: number;
+ name: string;
+ checked: boolean;
+ onPress: (id: number) => void;
+};
+
+function ChildFilter({ id, name, checked, onPress }: ChildFilterProps) {
+ return (
+ onPress(id)}
+ iconType="material-community"
+ checkedIcon="checkbox-marked"
+ uncheckedIcon="checkbox-blank-outline"
+ checkedColor="black"
+ />
+ );
+}
+
+export default memo(ChildFilter);
diff --git a/src/components/FilterModal/FilterModal.tsx b/src/components/FilterModal/FilterModal.tsx
index c2ded653..16fc7263 100644
--- a/src/components/FilterModal/FilterModal.tsx
+++ b/src/components/FilterModal/FilterModal.tsx
@@ -1,11 +1,14 @@
import { BottomSheet, CheckBox } from '@rneui/themed';
-import { useState } from 'react';
+import { useCallback, useState } from 'react';
import { View, Text, ScrollView, Pressable } from 'react-native';
+import { FlatList } from 'react-native-gesture-handler';
import { SafeAreaProvider } from 'react-native-safe-area-context';
-import 'react-native-gesture-handler';
+import ChildFilter from './ChildFilter';
+import ParentFilter from './ParentFilter';
import styles from './styles';
import Icon from '../../../assets/icons';
+import { TagFilter, useFilter } from '../../utils/FilterContext';
type FilterModalProps = {
isVisible: boolean;
@@ -13,33 +16,32 @@ type FilterModalProps = {
title: string;
};
+export enum CATEGORIES {
+ GENRE = 'genre-medium',
+ TOPIC = 'topic',
+ TONE = 'tone',
+}
+
function FilterModal({ isVisible, setIsVisible, title }: FilterModalProps) {
- const [checked1, toggleChecked1] = useState(false);
- const [checked2, toggleChecked2] = useState(false);
- const [checked3, toggleChecked3] = useState(false);
+ const { dispatch, filters } = useFilter();
- const genres = [
- {
- title: 'Fiction',
- state: checked1,
- setState: toggleChecked1,
+ const toggleParentFilter = useCallback(
+ (id: number) => {
+ dispatch({ type: 'TOGGLE_MAIN_GENRE', mainGenreId: id });
},
- {
- title: 'Erasure & Found Poetry',
- state: checked2,
- setState: toggleChecked2,
- },
- {
- title: 'Non-Fiction',
- state: checked3,
- setState: toggleChecked3,
+ [dispatch],
+ );
+
+ const toggleChildFilter = useCallback(
+ (id: number) => {
+ dispatch({ type: 'TOGGLE_FILTER', id });
},
- ];
+ [dispatch],
+ );
return (
{title}
-
- {genres.map(item => {
+ {
+ const [_, parentFilter] = item;
return (
- item.setState(!item.state)}
- iconType="material-community"
- checkedIcon="checkbox-marked"
- uncheckedIcon="checkbox-blank-outline"
- checkedColor="black"
- />
+ <>
+
+
+ {
+ return (
+
+ );
+ }}
+ />
+ >
);
- })}
-
+ }}
+ />
diff --git a/src/components/FilterModal/ParentFilter.tsx b/src/components/FilterModal/ParentFilter.tsx
new file mode 100644
index 00000000..d763e0a5
--- /dev/null
+++ b/src/components/FilterModal/ParentFilter.tsx
@@ -0,0 +1,26 @@
+import { CheckBox } from '@rneui/themed';
+import { memo } from 'react';
+
+type ParentFilterProps = {
+ id: number;
+ name: string;
+ checked: boolean;
+ onPress: (id: number) => void;
+};
+
+function ParentFilter({ id, name, checked, onPress }: ParentFilterProps) {
+ return (
+ onPress(id)}
+ iconType="material-community"
+ checkedIcon="checkbox-marked"
+ uncheckedIcon="checkbox-blank-outline"
+ checkedColor="black"
+ />
+ );
+}
+
+export default memo(ParentFilter);
diff --git a/src/components/PreviewCard/PreviewCard.tsx b/src/components/PreviewCard/PreviewCard.tsx
index 71f3589b..53164d4e 100644
--- a/src/components/PreviewCard/PreviewCard.tsx
+++ b/src/components/PreviewCard/PreviewCard.tsx
@@ -1,6 +1,6 @@
import * as cheerio from 'cheerio';
import { Image } from 'expo-image';
-import React, { useEffect, useState } from 'react';
+import { useEffect, useState } from 'react';
import {
GestureResponderEvent,
Pressable,
@@ -24,7 +24,7 @@ type PreviewCardProps = {
storyId: number;
author: string;
authorImage: string;
- defaultSavedStoriesState?: boolean;
+ defaultSavedStoriesState?: boolean | null;
excerpt: { html: string };
tags: string[];
reactions?: string[] | null;
@@ -39,7 +39,7 @@ function PreviewCard({
authorImage,
excerpt,
tags,
- defaultSavedStoriesState = false,
+ defaultSavedStoriesState = null,
pressFunction,
reactions: preloadedReactions = null,
}: PreviewCardProps) {
@@ -54,7 +54,7 @@ function PreviewCard({
(async () => {
const temp = await fetchAllReactionsToStory(storyId);
if (temp != null) {
- setReactions(temp.map(r => r.reaction));
+ setReactions(temp.filter(r => r != null));
return;
}
setReactions([]);
@@ -101,7 +101,7 @@ function PreviewCard({
-
+
{(tags?.length ?? 0) > 0 && (
diff --git a/src/components/ReactionDisplay/ReactionDisplay.tsx b/src/components/ReactionDisplay/ReactionDisplay.tsx
index 6b70eb1e..22f490b0 100644
--- a/src/components/ReactionDisplay/ReactionDisplay.tsx
+++ b/src/components/ReactionDisplay/ReactionDisplay.tsx
@@ -2,20 +2,27 @@ import { Text, View } from 'react-native';
import styles from './styles';
import Emoji from 'react-native-emoji';
import globalStyles from '../../styles/globalStyles';
+import { Channel, usePubSub } from '../../utils/PubSubContext';
+import { useEffect, useState } from 'react';
type ReactionDisplayProps = {
reactions: (string | null)[];
+ storyId: number;
};
-function ReactionDisplay({ reactions }: ReactionDisplayProps) {
- const cleanedReactions = reactions.filter(reaction => reaction !== null);
- const reactionColors: Record = {
- heart: '#FFCCCB',
- clap: '#FFD580',
- cry: '#89CFF0',
- hugging_face: '#ffc3bf',
- muscle: '#eddcf7',
- };
+const reactionColors: Record = {
+ heart: '#FFCCCB',
+ clap: '#FFD580',
+ cry: '#89CFF0',
+ hugging_face: '#ffc3bf',
+ muscle: '#eddcf7',
+};
+
+function ReactionDisplay({ reactions, storyId }: ReactionDisplayProps) {
+ const { channels, getPubSubValue } = usePubSub();
+ const [reactionCount, setReactionCount] = useState(0);
+
+ const cleanedReactions = reactions.filter(reaction => reaction != null);
const defaultColor = reactionColors['heart'];
const setOfReactions = [...cleanedReactions];
setOfReactions.push('heart');
@@ -23,6 +30,24 @@ function ReactionDisplay({ reactions }: ReactionDisplayProps) {
setOfReactions.push('muscle');
const reactionDisplay = [...new Set(setOfReactions)].slice(0, 3);
+ const serverReactionCount = cleanedReactions?.length ?? 0;
+
+ useEffect(() => {
+ setReactionCount(serverReactionCount);
+ }, [reactions]);
+
+ useEffect(() => {
+ const value = getPubSubValue(Channel.REACTIONS, storyId);
+ if (value == undefined) {
+ return;
+ }
+
+ if (value) {
+ setReactionCount(serverReactionCount + 1);
+ } else {
+ setReactionCount(serverReactionCount);
+ }
+ }, [channels[Channel.REACTIONS][storyId]]);
return (
{reactionDisplay.map(reaction => {
- if (reaction === null) return;
+ if (reaction == null) return;
return (
- {cleanedReactions?.length ?? 0}
+ {reactionCount}
diff --git a/src/components/ReactionPicker/ReactionPicker.tsx b/src/components/ReactionPicker/ReactionPicker.tsx
new file mode 100644
index 00000000..637a402e
--- /dev/null
+++ b/src/components/ReactionPicker/ReactionPicker.tsx
@@ -0,0 +1,81 @@
+import { faFaceSmile } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome';
+import { useEffect, useState } from 'react';
+import { View, TouchableOpacity } from 'react-native';
+
+import styles from './styles';
+import Emoji from 'react-native-emoji';
+import {
+ addReactionToStory,
+ deleteReactionToStory,
+} from '../../queries/reactions';
+import { useSession } from '../../utils/AuthContext';
+import { Channel, usePubSub } from '../../utils/PubSubContext';
+
+type ReactionPickerProps = {
+ storyId: number;
+};
+
+const ReactionPicker = ({ storyId }: ReactionPickerProps) => {
+ const { user } = useSession();
+ const { publish } = usePubSub();
+ const [showReactions, setShowReactions] = useState(false);
+ const [currentReaction, setCurrentReaction] = useState('');
+
+ const toggleReactions = () => setShowReactions(!showReactions);
+ const reactionMapping: Record = {
+ heart: 2,
+ clap: 3,
+ muscle: 4,
+ cry: 5,
+ hugging_face: 6,
+ };
+
+ const handleReactionPress = (reactionName: string) => {
+ if (currentReaction == reactionName) {
+ removeReaction(reactionName);
+ } else {
+ addReaction(reactionName);
+ }
+ };
+
+ const addReaction = (reactionName: string) => {
+ setCurrentReaction(reactionName);
+ publish(Channel.REACTIONS, storyId, true);
+
+ const reactionId = reactionMapping[reactionName];
+ addReactionToStory(user?.id, storyId, reactionId);
+ };
+
+ const removeReaction = (reactionName: string) => {
+ setCurrentReaction('');
+ publish(Channel.REACTIONS, storyId, false);
+
+ const reactionId = reactionMapping[reactionName];
+ deleteReactionToStory(user?.id, storyId, reactionId);
+ };
+
+ return (
+
+
+
+
+ {showReactions && (
+ <>
+
+ {Object.keys(reactionMapping).map((reaction, i) => (
+ handleReactionPress(reaction)}
+ >
+
+
+ ))}
+ >
+ )}
+
+
+ );
+};
+
+export default ReactionPicker;
diff --git a/src/components/ReactionPicker/styles.ts b/src/components/ReactionPicker/styles.ts
new file mode 100644
index 00000000..5a5a7052
--- /dev/null
+++ b/src/components/ReactionPicker/styles.ts
@@ -0,0 +1,29 @@
+import { StyleSheet } from 'react-native';
+
+import colors from '../../styles/colors';
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ justifyContent: 'flex-end',
+ // flexDirection: 'row'
+ },
+ reactionView: {
+ borderRadius: 20,
+ padding: 10,
+ alignSelf: 'center',
+ marginBottom: 10,
+ },
+ reactionsContainer: {
+ flexDirection: 'row',
+ gap: 5,
+ justifyContent: 'space-between',
+ padding: 10,
+ position: 'absolute', // Positioning the container above the toggle button
+ bottom: 50,
+ backgroundColor: '#D9D9D9',
+ borderRadius: 20,
+ },
+});
+
+export default styles;
diff --git a/src/components/SaveStoryButton/SaveStoryButton.tsx b/src/components/SaveStoryButton/SaveStoryButton.tsx
index be44ebb9..307d1d7e 100644
--- a/src/components/SaveStoryButton/SaveStoryButton.tsx
+++ b/src/components/SaveStoryButton/SaveStoryButton.tsx
@@ -4,7 +4,7 @@ import {
deleteUserStoryToReadingList,
isStoryInReadingList,
} from '../../queries/savedStories';
-import { usePubSub } from '../../utils/PubSubContext';
+import { Channel, usePubSub } from '../../utils/PubSubContext';
import { useSession } from '../../utils/AuthContext';
import { Image } from 'expo-image';
import { TouchableOpacity } from 'react-native-gesture-handler';
@@ -25,7 +25,7 @@ export default function SaveStoryButton({
const [storyIsSaved, setStoryIsSaved] = useState(
defaultState,
);
- const { channels, initializeChannel, publish } = usePubSub();
+ const { publish, channels, getPubSubValue } = usePubSub();
useEffect(() => {
if (defaultState != null) {
@@ -34,20 +34,18 @@ export default function SaveStoryButton({
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] ?? false);
+ if (getPubSubValue(Channel.SAVED_STORIES, storyId) != null) {
+ setStoryIsSaved(getPubSubValue(Channel.SAVED_STORIES, storyId) ?? false);
}
- }, [channels[storyId]]);
+ }, [channels[Channel.SAVED_STORIES][storyId]]);
const saveStory = async (saved: boolean) => {
setStoryIsSaved(saved);
- publish(storyId, saved); // update other cards with this story
+ publish(Channel.SAVED_STORIES, storyId, saved); // update other cards with this story
if (saved) {
await addUserStoryToReadingList(user?.id, storyId);
diff --git a/src/queries/reactions.tsx b/src/queries/reactions.tsx
index ffe68c40..f72c9e00 100644
--- a/src/queries/reactions.tsx
+++ b/src/queries/reactions.tsx
@@ -2,7 +2,7 @@ import { Reactions } from './types';
import supabase from '../utils/supabase';
export async function addReactionToStory(
- input_profile_id: number,
+ input_profile_id: string | undefined,
input_story_id: number,
input_reaction_id: number,
): Promise {
@@ -22,7 +22,7 @@ export async function addReactionToStory(
}
export async function deleteReactionToStory(
- input_profile_id: number,
+ input_profile_id: string | undefined,
input_story_id: number,
input_reaction_id: number,
): Promise {
@@ -43,17 +43,18 @@ export async function deleteReactionToStory(
export async function fetchAllReactionsToStory(
storyId: number,
-): Promise {
+): Promise {
const { data, error } = await supabase.rpc('curr_get_reactions_for_story', {
- input_story_id: storyId,
+ story_id: storyId,
});
+
if (error) {
console.log(error);
throw new Error(
`An error occured when trying to fetch reactions to a story', ${error}`,
);
} else {
- return data as Reactions[];
+ return data[0].reactions;
}
}
diff --git a/src/utils/PubSubContext.tsx b/src/utils/PubSubContext.tsx
index fbb9ada6..9079389f 100644
--- a/src/utils/PubSubContext.tsx
+++ b/src/utils/PubSubContext.tsx
@@ -1,9 +1,17 @@
-import React, { createContext, useContext, useMemo, useState } from 'react';
+import React, { createContext, useContext, useState } from 'react';
+
+export enum Channel {
+ REACTIONS = 'reactions',
+ SAVED_STORIES = 'saved_stories',
+ FAVORITES = 'favorites',
+}
+
+type channel = Record;
export interface PubSubState {
- channels: Record;
- initializeChannel: (id: number) => void;
- publish: (id: number, message: boolean) => void;
+ channels: Record;
+ publish: (channel: Channel, id: number, message: boolean) => void;
+ getPubSubValue: (channel: Channel, id: number) => boolean | undefined;
}
const BooleanPubSubContext = createContext({} as PubSubState);
@@ -26,28 +34,26 @@ export function BooleanPubSubProvider({
}: {
children: React.ReactNode;
}) {
- const [channels, setChannels] = useState>(
- {},
- );
-
- const initializeChannel = (id: number) => {
- if (!(id in channels)) {
- setChannels({ ...channels, [id]: undefined });
- }
+ const [channels, setChannels] = useState>({
+ [Channel.FAVORITES]: {},
+ [Channel.REACTIONS]: {},
+ [Channel.SAVED_STORIES]: {},
+ });
+
+ const publish = (channel: Channel, id: number, message: boolean) => {
+ let thisChannel = { ...channels[channel], [id]: message };
+ setChannels({ ...channels, [channel]: thisChannel });
};
- const publish = (id: number, message: boolean) => {
- setChannels({ ...channels, [id]: message });
+ const getPubSubValue = (channel: Channel, id: number) => {
+ return channels[channel][id];
};
- const authContextValue = useMemo(
- () => ({
- channels,
- initializeChannel,
- publish,
- }),
- [channels],
- );
+ const authContextValue = {
+ channels,
+ publish,
+ getPubSubValue,
+ };
return (