Skip to content

Commit

Permalink
Merge pull request #857 from bounswe/MOBILE-828
Browse files Browse the repository at this point in the history
feat(mobile): connect quiz like feature to the backend on quiz pages
  • Loading branch information
YavizGuldalf authored Dec 15, 2024
2 parents 78b5b3a + eb9a19e commit 3778858
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 89 deletions.
47 changes: 34 additions & 13 deletions mobile/bulingo/app/(tabs)/quizzes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ const QuizFeed = () => {
if (reset) {
setQuizzes([]);
setPage(1);
console.log('reset');
}

try {
Expand All @@ -42,7 +41,6 @@ const QuizFeed = () => {
});

const data = await response.json();
console.log(data);
if (response.ok) {
const formattedResults = data.map((quiz: any) => ({
id: quiz.id,
Expand Down Expand Up @@ -73,9 +71,9 @@ const QuizFeed = () => {
setSearchTerm(text);
};

const filteredQuizzes = quizzes.filter((quiz: any) =>
quiz.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
quiz.author.toLowerCase().includes(searchTerm.toLowerCase())
const filteredQuizzes = quizzes.filter((quiz: any) => {
return quiz.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
quiz.author.toLowerCase().includes(searchTerm.toLowerCase()) }
);

// const loadMoreQuizzes = () => {
Expand All @@ -91,15 +89,38 @@ const QuizFeed = () => {
});
};

const handleLikePress = (quizId: number) => {
const updatedQuizzes = quizzes.map((quiz: any) => {
const handleLikePress = async (quizId: number) => {
const updatedQuizzes = await Promise.all( quizzes.map( async (quiz: any) => {
if (quiz.id === quizId) {
const liked = !quiz.liked;
const likes = liked ? quiz.likes + 1 : quiz.likes - 1;
return { ...quiz, liked, likes };
}
return quiz;
});
let data = '';
try {
const response = await TokenManager.authenticatedFetch(`/quiz/like/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
quiz_id: quizId,
}),
});

data = await response.json();
let liked = quiz.liked;
let likes = quiz.likes;
if (response.ok) {
liked = !quiz.liked;
likes = liked ? quiz.likes + 1 : quiz.likes - 1;
}
return { ...quiz, liked, likes };
}
catch(error: any)
{
setError('Failed to fetch quizzes. Please try again. Error: ' + JSON.stringify(data));
}

}
return quiz;
}));
setQuizzes(updatedQuizzes);
};

Expand Down
186 changes: 110 additions & 76 deletions mobile/bulingo/app/(tabs)/quizzes/quizResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import TokenManager from '@/app/TokenManager';

const { width, height } = Dimensions.get('window');



const QuizResults = () => {
const { resultUrl, quizId } = useLocalSearchParams<{ resultUrl: string, quizId: string }>();
const colorScheme = useColorScheme();
const styles = getStyles(colorScheme);
const [resultQuizLiked, setResultQuizLiked] = useState<boolean>(false);
const [reccQuizLiked, setReccQuizLiked] = useState<boolean>(false);

const [quizResult, setQuizResult] = useState<{
questions: {
Expand Down Expand Up @@ -57,6 +57,38 @@ const QuizResults = () => {
liked: boolean;
} | null>(null);


const handleLikePress = async (liked: any, quizId: any) => {
let data = '';
try {
const response = await TokenManager.authenticatedFetch(`/quiz/like/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
quiz_id: quizId,
}),
});
console.log(quizId);
data = await response.json();
console.log(data);
if (response.ok) {
liked = !liked;
if (quizId === quizResult?.quiz_result.quiz.id) {
setResultQuizLiked(liked);
}
else {
setReccQuizLiked(liked);
};
}
}
catch(error: any)
{
setError('Failed to fetch quizzes. Please try again. Error: ' + JSON.stringify(data));
}
};

useEffect(() => {
const fetchQuizResult = async () => {
setLoading(true);
Expand All @@ -67,14 +99,16 @@ const QuizResults = () => {
'Content-Type': 'application/json',
},
});

const url = resultUrl.split('/');
const resultNum = url[url.length - 1];
setQuizResultNum(resultNum)

if (response.ok) {
const data = await response.json();
setQuizResult(data);
setResultQuizLiked(data.quiz_result.is_liked);

} else {
const errorData = await response.json();
setError(errorData.detail || 'Failed to fetch quiz result.');
Expand Down Expand Up @@ -110,7 +144,7 @@ const QuizResults = () => {
likes: quiz.like_count,
liked: quiz.is_liked,
};

setReccQuizLiked(quiz.is_liked);
setRecommendedQuiz(formattedResults);
setError(null);
} else {
Expand All @@ -126,6 +160,78 @@ const QuizResults = () => {

fetchQuizzes();
}, []);

const QuizResultsCard = (props: QuizResultsCardProps) => {
const { styles } = props.styles;
return (
<View style={[styles.resultsCard, styles.elevation]}>
<View style={styles.resultsTitleContainer}>
<Text style={styles.resultsTitle}>{props.quizName}</Text>
</View>
<View style={[styles.resultsScoreContainer, styles.elevation]}>
<Text style={styles.scoreText}>Score</Text>
<View style={[styles.scoreBox, styles.elevation]}>
<Text style={styles.scoreBoxText}>{props.score}/{props.maxScore}</Text>
</View>
</View>
<View style={styles.bottomMessageContainer}>
<Text style={styles.bottomMessage}>Congrats!</Text>
<Text style={styles.bottomMessage}>Keep it up!</Text>
</View>
<View style={styles.resultsBottomContainer}>
<View style={styles.resultsTagsContainer}>
{props.tags.map((item, index) => (
<View style={styles.tagBox} key={index}>
<Text style={styles.tagText}>{item}</Text>
</View>
))}
</View>
<TouchableOpacity onPress={() => handleLikePress(resultQuizLiked, quizResult?.quiz_result.quiz.id)}>
<Image source={resultQuizLiked ? require('@/assets/images/like-2.png') : require('@/assets/images/like-1.png')} style={styles.icon} />
</TouchableOpacity>

<TouchableOpacity>
<Image style={[styles.bottomButtonBookmark, {borderWidth: 0}]} source={require('@/assets/images/bookmark-icon.png')}/>
</TouchableOpacity>
</View>
</View>
);
};

const QuizCard = (props: {
name: string;
desc: string;
author: string;
tags: string[];
styles: any;
id?: number;
}) => {
const { styles } = props.styles;
return (
<TouchableOpacity
style={[styles.quizItem, styles.elevation]}
onPress={() => router.navigate({
pathname: '/(tabs)/quizzes/quizDetails',
params:{id : props?.id},})}
>
<View style={styles.quizInfo}>
<Text style={styles.quizTitle}>{props.name}</Text>
<Text style={styles.quizDescription}>{props.desc}</Text>
<Text style={styles.quizAuthor}>by {props.author}</Text>
<Text style={styles.quizLevel}>{props.tags}</Text>
</View>
<View style={styles.quizActions}>
<TouchableOpacity style={styles.likeButton} onPress={() => handleLikePress(reccQuizLiked, props.id)}>
<Image source={reccQuizLiked ? require('@/assets/images/like-2.png') : require('@/assets/images/like-1.png')} style={styles.icon} />
</TouchableOpacity>

<TouchableOpacity style={styles.bookmarkButton}>
<Image source={require('@/assets/images/bookmark-icon.png')} style={styles.icon} />
</TouchableOpacity>
</View>
</TouchableOpacity>
);
};


return (
Expand Down Expand Up @@ -207,78 +313,6 @@ export type QuizResultsCardProps = {
id?: number,
}

export const QuizResultsCard = (props: QuizResultsCardProps) => {
const { styles } = props.styles;
return (
<View style={[styles.resultsCard, styles.elevation]}>
<View style={styles.resultsTitleContainer}>
<Text style={styles.resultsTitle}>{props.quizName}</Text>
</View>
<View style={[styles.resultsScoreContainer, styles.elevation]}>
<Text style={styles.scoreText}>Score</Text>
<View style={[styles.scoreBox, styles.elevation]}>
<Text style={styles.scoreBoxText}>{props.score}/{props.maxScore}</Text>
</View>
</View>
<View style={styles.bottomMessageContainer}>
<Text style={styles.bottomMessage}>Congrats!</Text>
<Text style={styles.bottomMessage}>Keep it up!</Text>
</View>
<View style={styles.resultsBottomContainer}>
<View style={styles.resultsTagsContainer}>
{props.tags.map((item, index) => (
<View style={styles.tagBox} key={index}>
<Text style={styles.tagText}>{item}</Text>
</View>
))}
</View>
<TouchableOpacity>
<Image style={styles.bottomButtonLike} source={require('@/assets/images/like-1.png')}/>
</TouchableOpacity>

<TouchableOpacity>
<Image style={[styles.bottomButtonBookmark, {borderWidth: 0}]} source={require('@/assets/images/bookmark-icon.png')}/>
</TouchableOpacity>
</View>
</View>
);
};

export const QuizCard = (props: {
name: string;
desc: string;
author: string;
tags: string[];
styles: any;
id?: number;
}) => {
const { styles } = props.styles;
return (
<TouchableOpacity
style={[styles.quizItem, styles.elevation]}
onPress={() => router.navigate({
pathname: '/(tabs)/quizzes/quizDetails',
params:{id : props?.id},})}
>
<View style={styles.quizInfo}>
<Text style={styles.quizTitle}>{props.name}</Text>
<Text style={styles.quizDescription}>{props.desc}</Text>
<Text style={styles.quizAuthor}>by {props.author}</Text>
<Text style={styles.quizLevel}>{props.tags}</Text>
</View>
<View style={styles.quizActions}>
<TouchableOpacity style={styles.likeButton}>
<Image source={require('@/assets/images/like-1.png')} style={styles.icon} />

</TouchableOpacity>

<TouchableOpacity style={styles.bookmarkButton}>
<Image source={require('@/assets/images/bookmark-icon.png')} style={styles.icon} />
</TouchableOpacity>
</View>
</TouchableOpacity>
);
};

const getStyles = (colorScheme: any) => {
const isDark = colorScheme === 'dark';
Expand Down

0 comments on commit 3778858

Please sign in to comment.