Skip to content

Commit

Permalink
Added favorite button and updated styling
Browse files Browse the repository at this point in the history
  • Loading branch information
jjstnlee committed Apr 21, 2024
1 parent 85549e7 commit b09f834
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 26 deletions.
Binary file removed assets/save_story.png
Binary file not shown.
Binary file removed assets/saved_story.png
Binary file not shown.
22 changes: 22 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,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",
Expand Down Expand Up @@ -68,11 +69,11 @@
"react-native-vector-icons": "^10.0.2",
"react-scroll-to-top": "^3.0.0",
"use-debounce": "^10.0.0",
"validator": "^13.11.0",
"expo-image": "~1.3.5"
"validator": "^13.11.0"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@iconify/react": "^4.1.1",
"@types/react": "~18.2.14",
"@types/react-native": "^0.72.3",
"@types/react-native-htmlview": "^0.16.1",
Expand Down
31 changes: 18 additions & 13 deletions src/app/(tabs)/story/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { RenderHTML } from 'react-native-render-html';
import { SafeAreaView } from 'react-native-safe-area-context';

import styles from './styles';
import FavoriteStoryButton from '../../../components/FavoriteStoryButton/FavoriteStoryButton';
import SaveStoryButton from '../../../components/SaveStoryButton/SaveStoryButton';
import { fetchStory } from '../../../queries/stories';
import { Story } from '../../../queries/types';
import colors from '../../../styles/colors';
Expand Down Expand Up @@ -113,20 +115,23 @@ function StoryScreen() {
</View>
)}
/>

<Button
textColor="black"
buttonColor={colors.gwnOrange}
icon="share"
onPress={onShare}
style={{ width: 125, marginBottom: 16, borderRadius: 10 }}
>
<Text
style={[globalStyles.bodyUnderline, styles.shareButtonText]}
<View style={styles.options}>
<SaveStoryButton storyId={parseInt(storyId as string, 10)} />
<FavoriteStoryButton storyId={parseInt(storyId as string, 10)} />
<Button
textColor="black"
buttonColor={colors.gwnOrange}
icon="share"
onPress={onShare}
style={{ width: 125, marginBottom: 16, borderRadius: 10 }}
>
Share Story
</Text>
</Button>
<Text
style={[globalStyles.bodyUnderline, styles.shareButtonText]}
>
Share Story
</Text>
</Button>
</View>
</View>

<RenderHTML
Expand Down
6 changes: 6 additions & 0 deletions src/app/(tabs)/story/styles.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { StyleSheet } from 'react-native';

import colors from '../../../styles/colors';

const styles = StyleSheet.create({
Expand Down Expand Up @@ -57,6 +58,11 @@ const styles = StyleSheet.create({
process: {
marginBottom: 16,
},
options: {
flex: 1,
flexDirection: 'row',
justifyContent: 'flex-end',
},
});

export default styles;
63 changes: 63 additions & 0 deletions src/components/FavoriteStoryButton/FavoriteStoryButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { useEffect, useState } from 'react';
import { TouchableOpacity } from 'react-native-gesture-handler';
import Svg, { Path } from 'react-native-svg';

import {
addUserStoryToFavorites,
deleteUserStoryToFavorites,
isStoryInFavorites,
} from '../../queries/savedStories';
import { useSession } from '../../utils/AuthContext';

type FavoriteStoryButtonProps = {
storyId: number;
};

export default function FavoriteStoryButton({
storyId,
}: FavoriteStoryButtonProps) {
const { user } = useSession();
const [storyIsFavorited, setStoryIsFavorited] = useState(false);

useEffect(() => {
isStoryInFavorites(storyId, user?.id).then(storyInReadingList => {
setStoryIsFavorited(storyInReadingList);
});
}, [storyId]);

useEffect(() => {
isStoryInFavorites(storyId, user?.id).then(storyInReadingList =>
setStoryIsFavorited(storyInReadingList),
);
}, [storyId]);

const favoriteStory = async (favorited: boolean) => {
setStoryIsFavorited(favorited);

if (favorited) {
await addUserStoryToFavorites(user?.id, storyId);
} else {
await deleteUserStoryToFavorites(user?.id, storyId);
}
};

return (
<TouchableOpacity onPress={() => favoriteStory(!storyIsFavorited)}>
{storyIsFavorited ? (
<Svg width="30" height="30" viewBox="0 0 30 30" fill="none">
<Path
d="M15 24.5675L14.0525 23.7113C12.0125 21.8479 10.325 20.2525 8.99 18.925C7.655 17.5983 6.60125 16.4283 5.82875 15.415C5.05708 14.4008 4.5175 13.4837 4.21 12.6637C3.90333 11.8429 3.75 11.0175 3.75 10.1875C3.75 8.5975 4.29 7.2625 5.37 6.1825C6.45 5.1025 7.785 4.5625 9.375 4.5625C10.475 4.5625 11.5063 4.84375 12.4688 5.40625C13.4313 5.96875 14.275 6.78667 15 7.86C15.725 6.78667 16.5687 5.96875 17.5312 5.40625C18.4937 4.84375 19.525 4.5625 20.625 4.5625C22.215 4.5625 23.55 5.1025 24.63 6.1825C25.71 7.2625 26.25 8.5975 26.25 10.1875C26.25 11.0175 26.0967 11.8425 25.79 12.6625C25.4825 13.4833 24.9429 14.4008 24.1712 15.415C23.3987 16.4283 22.3487 17.5983 21.0212 18.925C19.6946 20.2525 18.0029 21.8483 15.9462 23.7125L15 24.5675Z"
fill="#EB563B"
/>
</Svg>
) : (
<Svg width="30" height="30" viewBox="0 0 30 30" fill="none">
<Path
d="M15 24.5675L14.0525 23.7113C12.0125 21.8479 10.325 20.2525 8.99 18.925C7.655 17.5983 6.60125 16.4283 5.82875 15.415C5.05708 14.4008 4.5175 13.4837 4.21 12.6637C3.90333 11.8429 3.75 11.0175 3.75 10.1875C3.75 8.5975 4.29 7.2625 5.37 6.1825C6.45 5.1025 7.785 4.5625 9.375 4.5625C10.475 4.5625 11.5063 4.84375 12.4688 5.40625C13.4313 5.96875 14.275 6.78667 15 7.86C15.725 6.78667 16.5687 5.96875 17.5312 5.40625C18.4937 4.84375 19.525 4.5625 20.625 4.5625C22.215 4.5625 23.55 5.1025 24.63 6.1825C25.71 7.2625 26.25 8.5975 26.25 10.1875C26.25 11.0175 26.0967 11.8425 25.79 12.6625C25.4825 13.4833 24.9429 14.4008 24.1712 15.415C23.3987 16.4283 22.3487 17.5983 21.0212 18.925C19.6946 20.2525 18.0029 21.8483 15.9462 23.7125L15 24.5675ZM15 22.875C17 21.0667 18.6458 19.5183 19.9375 18.23C21.2292 16.9408 22.25 15.8221 23 14.8738C23.75 13.9254 24.2708 13.0858 24.5625 12.355C24.8542 11.6233 25 10.9008 25 10.1875C25 8.9375 24.5833 7.89583 23.75 7.0625C22.9167 6.22917 21.875 5.8125 20.625 5.8125C19.63 5.8125 18.7117 6.09667 17.87 6.665C17.0283 7.2325 16.275 8.08792 15.61 9.23125H14.39C13.7083 8.07292 12.9508 7.21333 12.1175 6.6525C11.2842 6.0925 10.37 5.8125 9.375 5.8125C8.14167 5.8125 7.10417 6.22917 6.2625 7.0625C5.42083 7.89583 5 8.9375 5 10.1875C5 10.9008 5.14583 11.6233 5.4375 12.355C5.72917 13.0867 6.25 13.9263 7 14.8738C7.75 15.8213 8.77083 16.9358 10.0625 18.2175C11.3542 19.4992 13 21.0517 15 22.875Z"
fill="black"
/>
</Svg>
)}
</TouchableOpacity>
);
}
24 changes: 16 additions & 8 deletions src/components/SaveStoryButton/SaveStoryButton.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import { useEffect, useState } from 'react';
import { TouchableOpacity } from 'react-native-gesture-handler';
import Svg, { Path } from 'react-native-svg';

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';
import { usePubSub } from '../../utils/PubSubContext';

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);
Expand Down Expand Up @@ -55,9 +53,19 @@ export default function SaveStoryButton({ storyId }: SaveStoryButtonProps) {
return (
<TouchableOpacity onPress={() => saveStory(!storyIsSaved)}>
{storyIsSaved ? (
<Image style={{ width: 30, height: 30 }} source={savedStoryImage} />
<Svg width="30" height="30" viewBox="0 0 30 30" fill="none">
<Path
d="M4.375 19.375V18.125H13.125V19.375H4.375ZM4.375 14.375V13.125H18.125V14.375H4.375ZM4.375 9.375V8.125H18.125V9.375H4.375ZM20.4375 22.885L16.865 19.3125L17.75 18.4275L20.4375 21.1013L25.75 15.7887L26.635 16.6875L20.4375 22.885Z"
fill="#703929"
/>
</Svg>
) : (
<Image style={{ width: 30, height: 30 }} source={saveStoryImage} />
<Svg width="30" height="30" viewBox="0 0 30 30" fill="none">
<Path
d="M4.375 19.375V18.125H13.125V19.375H4.375ZM4.375 14.375V13.125H18.125V14.375H4.375ZM4.375 9.375V8.125H18.125V9.375H4.375ZM20.625 24.375V19.375H15.625V18.125H20.625V13.125H21.875V18.125H26.875V19.375H21.875V24.375H20.625Z"
fill="black"
/>
</Svg>
)}
</TouchableOpacity>
);
Expand Down
24 changes: 21 additions & 3 deletions src/queries/savedStories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ async function fetchUserStories(
return [];
}

let storyData = [];
const storyData = [];
for (const storyObject of storyObjects) {
const storyId = storyObject['story_id'];
const { data, error } = await supabase.rpc('fetch_story', {
Expand Down Expand Up @@ -64,7 +64,7 @@ async function addUserStory(
) {
const { error } = await supabase
.from('saved_stories')
.upsert([{ user_id: user_id, story_id: story_id, name: name }])
.upsert([{ user_id, story_id, name }])
.select();

if (error) {
Expand Down Expand Up @@ -131,7 +131,7 @@ export async function isStoryInReadingList(
storyId: number,
userId: string | undefined,
): Promise<boolean> {
let { data, error } = await supabase.rpc('is_story_saved_for_user', {
const { data, error } = await supabase.rpc('is_story_saved_for_user', {
list_name: 'reading list',
story_db_id: storyId,
user_uuid: userId,
Expand All @@ -144,3 +144,21 @@ export async function isStoryInReadingList(

return data;
}

export async function isStoryInFavorites(
storyId: number,
userId: string | undefined,
): Promise<boolean> {
const { data, error } = await supabase.rpc('is_story_saved_for_user', {
list_name: 'favorites',
story_db_id: storyId,
user_uuid: userId,
});

if (error) {
console.error(error);
return false;
}

return data;
}

0 comments on commit b09f834

Please sign in to comment.