Skip to content

Commit

Permalink
feat(mobile): add feed to home page
Browse files Browse the repository at this point in the history
  • Loading branch information
atakanyasar committed Dec 10, 2024
1 parent 94135ed commit b5b25dc
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 0 deletions.
2 changes: 2 additions & 0 deletions mobile/app/(tabs)/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Button, ButtonText, VStack } from "@/components/ui";
import useAuthStore from "@/services/auth";
import { Ionicons } from "@expo/vector-icons";
import { router } from "expo-router";
import { Feed } from "@/components/Feed";

export default function HomeScreen() {
const auth = useAuthStore();
Expand Down Expand Up @@ -45,6 +46,7 @@ export default function HomeScreen() {
<ButtonText>Logout</ButtonText>
</Button>
)}
<Feed />
</VStack>
</ScrollView>
);
Expand Down
17 changes: 17 additions & 0 deletions mobile/components/Feed.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { TagList } from "@/components/TagList";
import { Divider, Text, VStack } from "@/components/ui";
import { QuestionList } from "./QuestionsList";
import { Divide } from "lucide-react-native";

export const Feed = () => {

return (
<VStack className="gap-8">
<Text className="text-2xl font-bold">Tags</Text>
<TagList searchQueryParams="" pagesize={3} />
<Divider />
<Text className="text-2xl font-bold">Latest Questions</Text>
<QuestionList pageSize={3} />
</VStack>
);
};
99 changes: 99 additions & 0 deletions mobile/components/QuestionsList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import React, { useState } from "react";
import { View, Text, Button, ActivityIndicator } from "react-native";
import { QuestionCard } from "@/components/QuestionCard";
import {
SearchQuestionsQueryParams,
useSearchQuestions,
SearchQuestionsResponse,
} from "@/services/api/programmingForumComponents";

interface QuestionListProps {
searchQueryParams?: string;
pageSize?: number;
difficultyFilter?: "EASY" | "MEDIUM" | "HARD";
tagFilter?: string;
}

export const QuestionList: React.FC<QuestionListProps> = ({
searchQueryParams = "",
pageSize = 10,
difficultyFilter,
tagFilter = "",
}) => {
const [page, setPage] = useState(1);

const q: SearchQuestionsQueryParams = {
q: searchQueryParams,
page,
pageSize,
...(difficultyFilter && { difficulty: difficultyFilter }),
...(tagFilter && { tags: tagFilter }),
};

// Fetch questions
const {
data: questionSearchData,
isLoading: isLoadingQuestions,
error: questionsError,
} = useSearchQuestions({
queryParams: q,
});

const isQuestionsResponseObject = (
data: SearchQuestionsResponse["data"]
): data is Exclude<SearchQuestionsResponse["data"], any[]> => {
return typeof data === "object" && !Array.isArray(data);
};

const questions =
questionSearchData && isQuestionsResponseObject(questionSearchData.data)
? questionSearchData.data.items
: [];
const totalPages =
questionSearchData && isQuestionsResponseObject(questionSearchData.data)
? questionSearchData.data.totalPages
: 0;

const handleNextPage = () => {
if (page < (totalPages ?? 0)) setPage((prev) => prev + 1);
};

const handlePreviousPage = () => {
if (page > 1) setPage((prev) => prev - 1);
};

return (
<View>
{isLoadingQuestions && <ActivityIndicator size="large" />}
{questionsError && <Text>Error: {questionsError.status}</Text>}
{questions && (
<View className="mt-4 grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-4">
{questions.map((question) => (
<QuestionCard
key={question.id}
id={String(question.id)}
title={question.title}
content={question.content}
votes={question.likeCount}
answerCount={question.commentCount}
author={question.author}
difficulty={question.difficulty}
highlighted={question.difficulty === "EASY"}
/>
))}
</View>
)}
<View style={{ flexDirection: "row", justifyContent: "space-between", marginTop: 16 }}>
<Button title="Previous" onPress={handlePreviousPage} disabled={page === 1} />
<Text>
Page {page} of {totalPages}
</Text>
<Button
title="Next"
onPress={handleNextPage}
disabled={page === totalPages || isLoadingQuestions}
/>
</View>
</View>
);
};
66 changes: 66 additions & 0 deletions mobile/components/TagList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { SearchTagsQueryParams, useSearchTags, SearchTagsResponse } from "@/services/api/programmingForumComponents";
import { View, Text, Button, ActivityIndicator } from "react-native";
import { useState } from "react";
import { TagCard } from "@/components/TagCard";
import React from "react";

interface TagListProps {
searchQueryParams?: string;
pagesize?: number;
}

export const TagList: React.FC<TagListProps> = ({ searchQueryParams="", pagesize=20}) => {
const [page, setPage] = useState(1);
const [pageSize] = useState(pagesize);

const q: SearchTagsQueryParams = {
q: searchQueryParams,
page,
pageSize,
};

// Fetch tags
const { data: tagSearchData, isLoading: isLoadingTags, error: tagsError } = useSearchTags({
queryParams: q,
});

const isTagsResponseObject = (
data: SearchTagsResponse["data"]
): data is Exclude<SearchTagsResponse["data"], any[]> => {
return typeof data === "object" && !Array.isArray(data);
};

const tags = tagSearchData && isTagsResponseObject(tagSearchData.data) ? tagSearchData.data.items : [];
const totalPages = tagSearchData && isTagsResponseObject(tagSearchData.data) ? tagSearchData.data.totalPages : 0;

const isLoading = isLoadingTags;

const handleNextPage = () => {
if (page < (totalPages ?? 0)) setPage((prev) => prev + 1);
};

const handlePreviousPage = () => {
if (page > 1) setPage((prev) => prev - 1);
};

return (
<View>
{isLoading && <ActivityIndicator size="large" />}
{tagsError && <Text>Error: {tagsError.status}</Text>}
{tags && (
<View className="mt-4 grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-4">
{tags
.map((tag) => (
<TagCard key={tag.tagId} tag={tag} />
)
)}
</View>
)}
<View style={{ flexDirection: "row", justifyContent: "space-between", marginTop: 16 }}>
<Button title="Previous" onPress={handlePreviousPage} disabled={page === 1} />
<Text>Page {page} of {totalPages}</Text>
<Button title="Next" onPress={handleNextPage} disabled={page === totalPages || isLoading} />
</View>
</View>
);
};

0 comments on commit b5b25dc

Please sign in to comment.