Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: sortable profile posts and comments #1782

Merged
merged 1 commit into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/features/feed/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ type InternalFeedType =
| "PostsSearch"
| "CommentsSearch"
| "CommunitiesSearch"
| "CommunitiesExplore";
| "CommunitiesExplore"
| "ProfilePosts"
| "ProfileComments";

export type AnyFeed =
| {
Expand Down
53 changes: 53 additions & 0 deletions src/routes/pages/profile/BaseProfileFeedItemsPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { IonButtons, IonTitle, IonToolbar } from "@ionic/react";
import { IonPage } from "@ionic/react";
import { IonBackButton } from "@ionic/react";
import { useParams } from "react-router-dom";

import { FetchFn } from "#/features/feed/Feed";
import PostCommentFeed, {
PostCommentItem,
} from "#/features/feed/PostCommentFeed";
import AppHeader from "#/features/shared/AppHeader";
import { useBuildGeneralBrowseLink } from "#/helpers/routes";
import FeedContent from "#/routes/pages/shared/FeedContent";

interface BaseProfileFeedItemsPageProps {
label: string;
fetchFn: FetchFn<PostCommentItem>;
sortComponent?: React.ReactNode;
}

export default function BaseProfileFeedItemsPage({
fetchFn,
sortComponent,
label,
}: BaseProfileFeedItemsPageProps) {
const buildGeneralBrowseLink = useBuildGeneralBrowseLink();
const { handle } = useParams<{ handle: string }>();

return (
<IonPage>
<AppHeader>
<IonToolbar>
<IonTitle>{label}</IonTitle>
<IonButtons slot="start">
<IonBackButton
// Kinda hacky since Ionic doesn't handle clipping
text={handle.length > 10 ? `${handle.slice(0, 10)}...` : handle}
defaultHref={buildGeneralBrowseLink(`/u/${handle}`)}
/>
</IonButtons>

{sortComponent && <IonButtons slot="end">{sortComponent}</IonButtons>}
</IonToolbar>
</AppHeader>
<FeedContent>
<PostCommentFeed
fetchFn={fetchFn}
filterHiddenPosts={false}
filterKeywordsAndWebsites={false}
/>
</FeedContent>
</IonPage>
);
}
58 changes: 58 additions & 0 deletions src/routes/pages/profile/ProfileFeedCommentsPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { CommentSortType, PostSortType } from "lemmy-js-client";
import { useParams } from "react-router-dom";

import CommentSort from "#/features/comment/CommentSort";
import { FetchFn } from "#/features/feed/Feed";
import { PostCommentItem } from "#/features/feed/PostCommentFeed";
import useFeedSort from "#/features/feed/sort/useFeedSort";
import useClient from "#/helpers/useClient";
import { LIMIT } from "#/services/lemmy";

import BaseProfileFeedItemsPage from "./BaseProfileFeedItemsPage";

export default function ProfileFeedCommentsPage() {
const client = useClient();
const { handle } = useParams<{ handle: string }>();

const [sort, setSort] = useFeedSort(
"comments",
{
internal: `ProfileComments`,
},
"New",
);

const fetchFn: FetchFn<PostCommentItem> = async (pageData, ...rest) => {
const { comments } = await client.getPersonDetails(
{
...pageData,
limit: LIMIT,
username: handle,
sort: sort ? convertCommentSortToPostSort(sort) : "New",
},
...rest,
);

return comments;
};

return (
<BaseProfileFeedItemsPage
label="Comments"
fetchFn={fetchFn}
sortComponent={<CommentSort sort={sort} setSort={setSort} />}
/>
);
}

function convertCommentSortToPostSort(sort: CommentSortType): PostSortType {
switch (sort) {
case "Controversial":
case "Hot":
case "New":
case "Old":
return sort;
case "Top":
return "TopAll";
}
}
90 changes: 0 additions & 90 deletions src/routes/pages/profile/ProfileFeedItemsPage.tsx

This file was deleted.

45 changes: 45 additions & 0 deletions src/routes/pages/profile/ProfileFeedPostsPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useParams } from "react-router-dom";

import { FetchFn } from "#/features/feed/Feed";
import { PostCommentItem } from "#/features/feed/PostCommentFeed";
import PostSort from "#/features/feed/PostSort";
import useFeedSort from "#/features/feed/sort/useFeedSort";
import useClient from "#/helpers/useClient";
import { LIMIT } from "#/services/lemmy";

import BaseProfileFeedItemsPage from "./BaseProfileFeedItemsPage";

export default function ProfileFeedPostsPage() {
const client = useClient();
const { handle } = useParams<{ handle: string }>();

const [sort, setSort] = useFeedSort(
"posts",
{
internal: `ProfilePosts`,
},
"New",
);

const fetchFn: FetchFn<PostCommentItem> = async (pageData, ...rest) => {
const { posts } = await client.getPersonDetails(
{
...pageData,
limit: LIMIT,
username: handle,
sort: sort ?? "New",
},
...rest,
);

return posts;
};

return (
<BaseProfileFeedItemsPage
label="Posts"
fetchFn={fetchFn}
sortComponent={<PostSort sort={sort} setSort={setSort} />}
/>
);
}
31 changes: 31 additions & 0 deletions src/routes/pages/profile/ProfileFeedSavedPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useParams } from "react-router-dom";

import { FetchFn } from "#/features/feed/Feed";
import { PostCommentItem } from "#/features/feed/PostCommentFeed";
import { sortPostCommentByPublished } from "#/helpers/lemmy";
import useClient from "#/helpers/useClient";
import { LIMIT } from "#/services/lemmy";

import BaseProfileFeedItemsPage from "./BaseProfileFeedItemsPage";

export default function ProfileFeedSavedPage() {
const { handle } = useParams<{ handle: string }>();
const client = useClient();

const fetchFn: FetchFn<PostCommentItem> = async (pageData, ...rest) => {
const { comments, posts } = await client.getPersonDetails(
{
...pageData,
limit: LIMIT,
username: handle,
sort: "New",
saved_only: true,
},
...rest,
);

return [...comments, ...posts].sort(sortPostCommentByPublished);
};

return <BaseProfileFeedItemsPage label="Saved" fetchFn={fetchFn} />;
}
39 changes: 39 additions & 0 deletions src/routes/pages/profile/ProfileFeedVotedPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { GetComments, GetPosts } from "lemmy-js-client";

import { FetchFn } from "#/features/feed/Feed";
import { PostCommentItem } from "#/features/feed/PostCommentFeed";
import { sortPostCommentByPublished } from "#/helpers/lemmy";
import useClient from "#/helpers/useClient";
import { LIMIT } from "#/services/lemmy";

import BaseProfileFeedItemsPage from "./BaseProfileFeedItemsPage";

interface ProfileFeedVotedPageProps {
type: "Upvoted" | "Downvoted";
}

export default function ProfileFeedVotedPage({
type,
}: ProfileFeedVotedPageProps) {
const client = useClient();

const fetchFn: FetchFn<PostCommentItem> = async (pageData, ...rest) => {
const requestPayload: GetPosts & GetComments = {
...pageData,
limit: Math.floor(LIMIT / 2),
sort: "New",
liked_only: type === "Upvoted",
disliked_only: type === "Downvoted",
show_read: true,
};

const [{ posts }, { comments }] = await Promise.all([
client.getPosts(requestPayload, ...rest),
client.getComments(requestPayload, ...rest),
]);

return [...comments, ...posts].sort(sortPostCommentByPublished);
};

return <BaseProfileFeedItemsPage label={type} fetchFn={fetchFn} />;
}
15 changes: 9 additions & 6 deletions src/routes/tabs/general.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
import Route from "#/routes/common/Route";
import ConversationPage from "#/routes/pages/inbox/ConversationPage";
import PostDetail from "#/routes/pages/posts/PostPage";
import ProfileFeedCommentsPage from "#/routes/pages/profile/ProfileFeedCommentsPage";
import ProfileFeedHiddenPostsPage from "#/routes/pages/profile/ProfileFeedHiddenPostsPage";
import ProfileFeedItemsPage from "#/routes/pages/profile/ProfileFeedItemsPage";
import ProfileFeedPostsPage from "#/routes/pages/profile/ProfileFeedPostsPage";
import ProfileFeedSavedPage from "#/routes/pages/profile/ProfileFeedSavedPage";
import ProfileFeedVotedPage from "#/routes/pages/profile/ProfileFeedVotedPage";
import UserPage from "#/routes/pages/profile/UserPage";
import SearchFeedResultsPage from "#/routes/pages/search/results/SearchFeedResultsPage";
import CommentsPage from "#/routes/pages/shared/CommentsPage";
Expand Down Expand Up @@ -74,22 +77,22 @@ export default [
<UserPage />
</Route>,
<Route exact path="/:tab/:actor/u/:handle/posts">
<ProfileFeedItemsPage type="Posts" />
<ProfileFeedPostsPage />
</Route>,
<Route exact path="/:tab/:actor/u/:handle/comments">
<ProfileFeedItemsPage type="Comments" />
<ProfileFeedCommentsPage />
</Route>,
<Route exact path="/:tab/:actor/u/:handle/saved">
<ProfileFeedItemsPage type="Saved" />
<ProfileFeedSavedPage />
</Route>,
<Route exact path="/:tab/:actor/u/:handle/hidden">
<ProfileFeedHiddenPostsPage />
</Route>,
<Route exact path="/:tab/:actor/u/:handle/upvoted">
<ProfileFeedItemsPage type="Upvoted" />
<ProfileFeedVotedPage type="Upvoted" />
</Route>,
<Route exact path="/:tab/:actor/u/:handle/downvoted">
<ProfileFeedItemsPage type="Downvoted" />
<ProfileFeedVotedPage type="Downvoted" />
</Route>,
<Route exact path="/:tab/:actor/u/:handle/message">
<ConversationPage />
Expand Down
Loading