Skip to content

Commit

Permalink
Merge pull request #701 from bounswe/feature/700-sort-recommended-order
Browse files Browse the repository at this point in the history
[Mobile] Sort Recommended Order
  • Loading branch information
atakanyasar authored Dec 16, 2024
2 parents 42aae89 + 01bb3f1 commit 81c6965
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 39 deletions.
3 changes: 2 additions & 1 deletion mobile/app/question/[questionId].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
import { useEffect, useState } from "react";
import { ScrollView, View } from "react-native";
import placeholderProfile from "@/assets/images/placeholder_profile.png";
import { ContentWithSnippets } from "@/components/ContentWithSnippets";

export default function QuestionPage() {
const { questionId } = useLocalSearchParams();
Expand Down Expand Up @@ -329,7 +330,7 @@ export default function QuestionPage() {
</View>

<View className="rounded-lg bg-neutral-100 p-4">
<Text className="whitespace-pre-wrap">{question.content}</Text>
<ContentWithSnippets content={question.content} />
</View>

<Text className="text-2xl font-bold">Answers</Text>
Expand Down
64 changes: 54 additions & 10 deletions mobile/app/question/[questionId]/answer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,25 @@ import {
Icon,
Textarea,
TextareaInput,
Popover,
PopoverBackdrop,
PopoverContent,
PopoverArrow,
PopoverHeader,
PopoverCloseButton,
PopoverBody,
PopoverFooter,
} from "@/components/ui";
import { X } from "lucide-react-native";
import { X, InfoIcon } from "lucide-react-native";
import { ContentWithSnippets } from "@/components/ContentWithSnippets";

export default function NewAnswerPage() {
const { questionId } = useLocalSearchParams<{ questionId: string }>();
const { mutateAsync: createAnswer, status, error } = useCreateAnswer();
const router = useRouter();

const [content, setContent] = useState("");
const [preview, setPreview] = useState(false);
const contentLength = content.length;
const token = useAuthStore((state) => state.token);

Expand Down Expand Up @@ -68,15 +78,49 @@ export default function NewAnswerPage() {
<Icon as={X} />
</Button>
<View style={{ gap: 16 }} />
<Text style={{ fontSize: 20 }}>Write your answer</Text>
<Textarea>
<TextareaInput
value={content}
onChangeText={setContent}
placeholder="Write your answer here..."
maxLength={1000}
/>
</Textarea>
<HStack className="flex items-center justify-between">
<Text style={{ fontSize: 20 }}>Write your answer</Text>
<Popover
trigger={(triggerProps) => {
return (
<Button {...triggerProps} size="sm" variant="outline">
<Icon as={InfoIcon} />
</Button>
)
}}
onOpen={() => console.log("Popover opened")}
>
<PopoverBackdrop />
<PopoverContent>
<PopoverHeader>
<PopoverCloseButton />
</PopoverHeader>
<PopoverBody>
<ContentWithSnippets content="Writing Questions: We use Markdown for formatting questions. You can use standard Markdown syntax for headers, lists, links, etc. For a basic reference, you can check CommonMark." />
</PopoverBody>
<PopoverFooter />
</PopoverContent>
</Popover>
</HStack>
<HStack style={{ gap: 16 }}>
<Button onPress={() => setPreview(!preview)} variant="outline" size="sm">
<ButtonText>{preview ? "Edit" : "Preview"}</ButtonText>
</Button>
</HStack>
{preview ? (
<ContentWithSnippets content={content} />
) : (
<Textarea>
<TextareaInput
value={content}
onChangeText={setContent}
placeholder="Write your answer here..."
maxLength={1000}
/>
</Textarea>
)
}

<Text>{contentLength} / 1000</Text>
<Button onPress={handleSubmit} disabled={contentLength === 0} style={{ alignSelf: "flex-end" }}>
<ButtonText>Submit</ButtonText>
Expand Down
3 changes: 3 additions & 0 deletions mobile/app/question/new.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ export default function NewQuestionPage() {
return (
<ScrollView style={{ padding: 32, flex: 1, marginVertical: 16 }}>
<VStack style={{ gap: 16, flex: 1 }}>
<Button onPress={() => router.back()} style={{ alignSelf: "flex-start" }} variant={"outline"} size="sm">
<Icon as={X} />
</Button>
<Text style={{ fontSize: 24, fontWeight: "bold" }}>
Create New Question
</Text>
Expand Down
45 changes: 25 additions & 20 deletions mobile/app/tags/[tagId].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default function TagPage() {

const tag = data?.data;
const token = useAuthStore((s) => s.token);
const [tab, setTab] = useState<"top-rated" | "recent">("top-rated");
const [tab, setTab] = useState<"top_rated" | "recent" | "recommended">("top_rated");
const [difficultyFilter, setDifficultyFilter] = useState<DifficultyLevel>();

if (isLoading) {
Expand Down Expand Up @@ -122,23 +122,6 @@ export default function TagPage() {
</Button>
</Link>
)}
</HStack>
<HStack className="flex items-center justify-between">
<ButtonGroup className="flex items-center gap-2">
<Button
variant={tab === "top-rated" ? "solid" : "outline"}
onPress={() => setTab("top-rated")}
>
<ButtonText>Top-Rated</ButtonText>
</Button>
<Button
variant={tab === "recent" ? "solid" : "outline"}
onPress={() => setTab("recent")}
>
<ButtonText>Recent</ButtonText>
</Button>
</ButtonGroup>

<Select
className="w-1/3 ml-auto"
selectedValue={difficultyFilter}
Expand All @@ -162,13 +145,35 @@ export default function TagPage() {
</SelectPortal>
</Select>
</HStack>

<HStack className="flex items-center justify-between">
<ButtonGroup className="flex items-center gap-2">
<Button
variant={tab === "top_rated" ? "solid" : "outline"}
onPress={() => setTab("top_rated")}
>
<ButtonText>Top-Rated</ButtonText>
</Button>
<Button
variant={tab === "recent" ? "solid" : "outline"}
onPress={() => setTab("recent")}
>
<ButtonText>Recent</ButtonText>
</Button>
<Button
variant={tab === "recommended" ? "solid" : "outline"}
onPress={() => setTab("recommended")}
>
<ButtonText>Recommended</ButtonText>
</Button>
</ButtonGroup>
</HStack>

<QuestionListSearch
searchQueryParams=""
tagFilter={tag.tagId.toString()}
{...(difficultyFilter ? { difficultyFilter } : {})}
sortBy={tab === "top-rated" ? "TOP_RATED" : "RECENT"}
sortBy={tab}
pageSize={300}
/>
</VStack>
</View>
Expand Down
4 changes: 2 additions & 2 deletions mobile/components/Feed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ export const Feed = () => {
<Text className="text-2xl font-bold">Tags</Text>
<TagList searchQueryParams="" pagesize={3} />
<Divider />
<Text className="text-2xl font-bold">Latest Questions</Text>
<QuestionListSearch pageSize={3} />
<Text className="text-2xl font-bold">Recommended Questions</Text>
<QuestionListSearch pageSize={300} sortBy="recommended"/>
</VStack>
);
};
10 changes: 6 additions & 4 deletions mobile/components/QuestionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ interface QuestionListSearchProps {
pageSize?: number;
difficultyFilter?: "EASY" | "MEDIUM" | "HARD";
tagFilter?: string;
sortBy?: "RECENT" | "TOP_RATED";
sortBy?: "recommended" | "recent" | "top_rated";
}

export const QuestionListSearch: React.FC<QuestionListSearchProps> = ({
searchQueryParams = "",
pageSize = 10,
difficultyFilter,
tagFilter = "",
sortBy = "RECENT",
sortBy = "recommended",
}) => {
const [page, setPage] = useState(1);

Expand Down Expand Up @@ -97,10 +97,12 @@ export const QuestionListSearch: React.FC<QuestionListSearchProps> = ({
<View className="mt-4 grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-4">
{questions
.sort((a, b) => {
if (sortBy === "RECENT") {
if (sortBy === "recent") {
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
} else {
} else if (sortBy === "top_rated") {
return (b.upvoteCount - b.downvoteCount) - (a.upvoteCount - a.downvoteCount);
} else {
return 0;
}
})
.map((question) => (
Expand Down
6 changes: 6 additions & 0 deletions mobile/services/api/programmingForumComponents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1978,6 +1978,12 @@ export type SearchQuestionsQueryParams = {
* @default 20
*/
pageSize?: number;
/**
* Sorting type
*
* @default recommended
*/
sortBy?: string;
};

export type SearchQuestionsError = Fetcher.ErrorWrapper<{
Expand Down
5 changes: 3 additions & 2 deletions mobile/services/api/programmingForumSchemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export type UserProfileUpdate = {
};

/**
* @example {"id":1,"username":"john_doe","reputationPoints":100,"profilePicture":"@/assets/images/placeholder_profile.png","name":"John Doe"}
* @example {"id":1,"username":"john_doe","reputationPoints":100,"profilePicture":"https://placehold.co/640x640","name":"John Doe"}
*/
export type UserSummary = {
id: number;
Expand All @@ -121,7 +121,7 @@ export type NewQuestion = {
export type UpdateQuestion = {
title?: string;
content?: string;
tags?: string[];
tags?: number[];
};

/**
Expand All @@ -143,6 +143,7 @@ export type QuestionDetails = {
tags: TagSummary[];
likeCount: number;
dislikeCount: number;
difficulty: DifficultyLevel;
commentCount: number;
viewCount?: number;
bookmarked: boolean;
Expand Down

0 comments on commit 81c6965

Please sign in to comment.