Skip to content

Commit

Permalink
mobile: completely refactor reviews
Browse files Browse the repository at this point in the history
Fixes #201
  • Loading branch information
ericswpark committed Apr 21, 2024
1 parent 4a249de commit a1954bd
Show file tree
Hide file tree
Showing 5 changed files with 329 additions and 399 deletions.
98 changes: 98 additions & 0 deletions mobile/Backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import Genres from "./Constants/Genres";
import ID from "./Constants/ID";
import {
BACKEND_API_BOOK_SEARCH_URL,
BACKEND_API_CUSTOM_PROPERTY_DATA_URL,
BACKEND_API_READING_CHALLENGES,
BACKEND_API_REVIEW_URL,
BACKEND_API_URL,
} from "./Constants/URLs";

Expand Down Expand Up @@ -563,6 +565,102 @@ export default class Backend {
return (await account.get()).name;
};

public getReviews = async (book_id: string) => {
const review_docs = (
await databases.listDocuments(ID.mainDBID, ID.reviewsCollectionID, [
Query.equal("book", book_id),
])
).documents;

return review_docs;
};

public getReviewDoc = async (review_id: string) => {
const review_doc = await databases.getDocument(
ID.mainDBID,
ID.reviewsCollectionID,
review_id,
);

return review_doc;
};

public getReview = async (review_id: string) => {
const review_doc = await this.getReviewDoc(review_id);

const username: string | undefined = await this.getUsername({
user_id: review_doc.user_id,
});

return {
username: username ?? "Anonymous",
...review_doc,
};
};

public getReviewVotes = async (review_id: string) => {
const res = await fetch(`${BACKEND_API_REVIEW_URL}/${review_id}/vote`, {
method: "GET",
headers: new Headers({
"Content-Type": "application/json",
Authorization: "Bearer " + (await account.createJWT()).jwt,
}),
});
const res_json = await res.json();

return res_json.results;
};

public voteOnReview = async ({
review_id,
vote,
}: {
review_id: string;
vote: string;
}) => {
if (vote === "UPVOTE" || vote === "DOWNVOTE") {
const res = await fetch(`${BACKEND_API_REVIEW_URL}/${review_id}/vote`, {
method: "POST",
headers: new Headers({
"Content-Type": "application/json",
Authorization: "Bearer " + (await account.createJWT()).jwt,
}),
body: JSON.stringify({ vote }),
});
const res_json = await res.json();
return res_json;
} else {
const res = await fetch(`${BACKEND_API_REVIEW_URL}/${review_id}/vote`, {
method: "DELETE",
headers: new Headers({
"Content-Type": "application/json",
Authorization: "Bearer " + (await account.createJWT()).jwt,
}),
});
const res_json = await res.json();
return res_json;
}
};

public getCustomProperties = async (book_id: string) => {
const res = await fetch(
`${BACKEND_API_CUSTOM_PROPERTY_DATA_URL}?` +
new URLSearchParams({
book_id,
}),
{
method: "GET",
headers: new Headers({
"Content-Type": "application/json",
Authorization: "Bearer " + (await account.createJWT()).jwt,
}),
},
);
const res_json = await res.json();

return res_json.results.documents;
};

public sendNotification = async (
user_id: string,
type: any,
Expand Down
123 changes: 13 additions & 110 deletions mobile/Components/BookInfoModalReview/BookInfoModalReview.tsx
Original file line number Diff line number Diff line change
@@ -1,115 +1,19 @@
import Backend from "@/Backend";
import Colors from "@/Constants/Colors";
import ID from "@/Constants/ID";
import { BACKEND_API_URL } from "@/Constants/URLs";
import { client } from "@/appwrite";
import { Account, Databases, Query } from "appwrite";
import { useFocusEffect } from "@react-navigation/native";
import React from "react";
import { ActivityIndicator, FlatList, Text, View } from "react-native";
import FontAwesome from "react-native-vector-icons/FontAwesome";
import useSWR from "swr";
import BookInfoReviewItem from "../BookInfoReviewItem";
import { useFocusEffect } from "@react-navigation/native";

const account = new Account(client);
const databases = new Databases(client);

interface Review {
rating: number;
desc: string;
username: string;
id: string;
user_id: string;
upvotes: number;
downvotes: number;
userVote: string;
}

async function getReviews(book_id: string): Promise<Review[]> {
const reviews = [];
const documents = (
await databases.listDocuments(ID.mainDBID, ID.reviewsCollectionID, [
Query.equal("book", book_id),
])
).documents;

for (const document of documents) {
try {
const [review_data, votesData] = await Promise.all([
databases.getDocument(
ID.mainDBID,
ID.reviewsCollectionID,
document.$id,
),
fetchVotesData(document.$id),
]);

const response = await fetchUserData(review_data.user_id as string);

const name: string = response.name ?? "Anonymous";
const upvotes: number = votesData ? votesData.results.upvotes : 0;
const downvotes: number = votesData ? votesData.results.downvotes : 0;
const userVote: string = votesData ? votesData.results.user_voted : "";
const review = {
rating: review_data.star_rating as number,
desc: review_data.description as string,
username: name,
id: document.$id,
user_id: review_data.user_id as string,
upvotes,
downvotes,
userVote,
};

reviews.push(review);
} catch (error) {
console.error("Error fetching review:", error);
}
}
return reviews;
}

interface UserDataResponse {
name: string | undefined;
}

async function fetchUserData(userId: string): Promise<UserDataResponse> {
const response = await fetch(`${BACKEND_API_URL}/v0/users/${userId}/name`, {
method: "GET",
headers: new Headers({
"Content-Type": "application/json",
Authorization: "Bearer " + (await account.createJWT()).jwt,
}),
});
return response.json() as unknown as UserDataResponse;
}

async function fetchVotesData(reviewId: string) {
try {
const votesResponse = await fetch(
`${BACKEND_API_URL}/v0/reviews/${reviewId}/vote`,
{
method: "GET",
headers: new Headers({
"Content-Type": "application/json",
Authorization: "Bearer " + (await account.createJWT()).jwt,
}),
},
);

if (!votesResponse.ok) {
if (votesResponse.status == 404) return null;
console.error(await votesResponse.json());
}

return votesResponse.json();
} catch (error) {
console.error("Error fetching votes data:", error);
return null;
}
}
const backend = new Backend();

export const BookInfoModalReview = ({ bookInfo, navigation }) => {
const { data, error, isLoading, mutate } = useSWR(bookInfo.id, getReviews);
const { data, isLoading, mutate } = useSWR(
{ func: backend.getReviews, arg: bookInfo.id },
backend.swrFetcher,
);
const [averageRating, setAverageRating] = React.useState(0);

useFocusEffect(
Expand All @@ -119,17 +23,17 @@ export const BookInfoModalReview = ({ bookInfo, navigation }) => {
);

React.useEffect(() => {
if (!isLoading && data) {
if (data) {
let totalRating = 0;
data.forEach((review) => {
totalRating += review.rating;
data.forEach((review_doc: any) => {
totalRating += review_doc.star_rating;
});
const avgRating = totalRating / data.length;
setAverageRating(avgRating);
}
}, [data, isLoading]);
}, [data]);

if (isLoading || !data) {
if (isLoading) {
return (
<View style={{ flex: 1, backgroundColor: "white" }}>
<ActivityIndicator />
Expand Down Expand Up @@ -178,9 +82,8 @@ export const BookInfoModalReview = ({ bookInfo, navigation }) => {
)}
renderItem={({ item }) => (
<BookInfoReviewItem
bookInfo={bookInfo}
navigation={navigation}
item={item}
review_id={item.$id}
/>
)}
/>
Expand Down
Loading

0 comments on commit a1954bd

Please sign in to comment.