[redditmon] inbox: support subreddit filtering (#1664)
This commit is contained in:
parent
afac121e9a
commit
056fe0d266
|
|
@ -16,6 +16,7 @@ import { usePostsContext } from '~/components/posts/PostsContext';
|
|||
import { ShortcutAction } from '~/config/shortcuts';
|
||||
import type { PostListTab } from '~/types';
|
||||
|
||||
import SubredditFilter from '../SubredditFilter';
|
||||
import FetchPostButton from './FetchPostButton';
|
||||
import PostItem from './PostItem';
|
||||
|
||||
|
|
@ -43,7 +44,9 @@ export default function PostList() {
|
|||
isLoading,
|
||||
posts,
|
||||
selectedPostId,
|
||||
selectedSubreddits,
|
||||
setActiveTab,
|
||||
setSelectedSubreddits,
|
||||
} = usePostsContext();
|
||||
|
||||
usePostTabShortcuts({ setActiveTab });
|
||||
|
|
@ -120,6 +123,12 @@ export default function PostList() {
|
|||
)}
|
||||
<FetchPostButton />
|
||||
</div>
|
||||
<div className="px-2 pb-2">
|
||||
<SubredditFilter
|
||||
selectedSubreddits={selectedSubreddits}
|
||||
onChange={setSelectedSubreddits}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-0 grow overflow-y-auto">
|
||||
{isLoading ? (
|
||||
|
|
|
|||
|
|
@ -40,8 +40,11 @@ type PostsContextType = {
|
|||
posts: Array<QueriedRedditPost>;
|
||||
// Selection state
|
||||
selectedPostId: string | null;
|
||||
// Subreddit filtering
|
||||
selectedSubreddits: Array<string>;
|
||||
setActiveTab: (tab: PostListTab) => void;
|
||||
setSelectedPostId: (id: string | null) => void;
|
||||
setSelectedSubreddits: (subreddits: Array<string>) => void;
|
||||
};
|
||||
|
||||
const PostsContext = createContext<PostsContextType | undefined>(undefined);
|
||||
|
|
@ -55,11 +58,15 @@ export function PostsProvider({
|
|||
}) {
|
||||
const [activeTab, setActiveTab] = useState<PostListTab>('ALL');
|
||||
const [selectedPostId, setSelectedPostId] = useState<string | null>(null);
|
||||
const [selectedSubreddits, setSelectedSubreddits] = useState<Array<string>>(
|
||||
[],
|
||||
);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const utils = trpc.useUtils();
|
||||
|
||||
const prevActiveTabRef = useRef<PostListTab>(activeTab);
|
||||
const prevSelectedSubredditsRef = useRef<Array<string>>(selectedSubreddits);
|
||||
|
||||
// Store navigation context before mutations to handle auto-navigation
|
||||
const navigationContextRef = useRef<{
|
||||
|
|
@ -147,7 +154,10 @@ export function PostsProvider({
|
|||
const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } =
|
||||
trpc.socialPosts.getPosts.useInfiniteQuery(
|
||||
{
|
||||
filter: { tab: activeTab },
|
||||
filter: {
|
||||
subreddits: selectedSubreddits.length > 0 ? selectedSubreddits : undefined,
|
||||
tab: activeTab
|
||||
},
|
||||
pagination: { limit: 20 },
|
||||
projectSlug,
|
||||
},
|
||||
|
|
@ -173,10 +183,14 @@ export function PostsProvider({
|
|||
|
||||
useEffect(() => {
|
||||
const hasTabChanged = prevActiveTabRef.current !== activeTab;
|
||||
const hasSubredditFilterChanged =
|
||||
JSON.stringify(prevSelectedSubredditsRef.current) !==
|
||||
JSON.stringify(selectedSubreddits);
|
||||
|
||||
prevActiveTabRef.current = activeTab;
|
||||
prevSelectedSubredditsRef.current = selectedSubreddits;
|
||||
|
||||
if (hasTabChanged) {
|
||||
if (hasTabChanged || hasSubredditFilterChanged) {
|
||||
setSelectedPostId(null);
|
||||
|
||||
return;
|
||||
|
|
@ -188,8 +202,8 @@ export function PostsProvider({
|
|||
setSelectedPostId(firstPostId);
|
||||
router.push(`/projects/${projectSlug}/posts/${firstPostId}`);
|
||||
}
|
||||
}, [posts, selectedPostId, isLoading, projectSlug, router, activeTab]);
|
||||
|
||||
}, [posts, selectedPostId, isLoading, projectSlug, router, activeTab, selectedSubreddits]);
|
||||
|
||||
// Auto-load next page when user reaches the last post
|
||||
useEffect(() => {
|
||||
if (
|
||||
|
|
@ -344,8 +358,10 @@ export function PostsProvider({
|
|||
markPostReplyStatus,
|
||||
posts,
|
||||
selectedPostId,
|
||||
selectedSubreddits,
|
||||
setActiveTab,
|
||||
setSelectedPostId,
|
||||
setSelectedSubreddits,
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
'use client';
|
||||
|
||||
import { MultiSelect } from '@mantine/core';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { trpc } from '~/hooks/trpc';
|
||||
import useCurrentProjectSlug from '~/hooks/useCurrentProjectSlug';
|
||||
|
||||
type Props = Readonly<{
|
||||
onChange: (selectedSubreddits: Array<string>) => void;
|
||||
selectedSubreddits: Array<string>;
|
||||
}>;
|
||||
|
||||
export default function SubredditFilter({
|
||||
onChange,
|
||||
selectedSubreddits,
|
||||
}: Props) {
|
||||
const projectSlug = useCurrentProjectSlug();
|
||||
const { data: project, isLoading } = trpc.project.get.useQuery({
|
||||
projectSlug,
|
||||
});
|
||||
|
||||
const availableSubreddits = useMemo(() => {
|
||||
console.info(project?.subredditKeywords);
|
||||
|
||||
const subreddits =
|
||||
project?.subredditKeywords?.flatMap(
|
||||
(group: { subreddits: Array<string> }) => group.subreddits,
|
||||
) || [];
|
||||
|
||||
return subreddits.map((subreddit: string) => ({
|
||||
label: `r/${subreddit}`,
|
||||
value: `r/${subreddit}`,
|
||||
}));
|
||||
}, [project?.subredditKeywords]);
|
||||
|
||||
if (isLoading || availableSubreddits.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<MultiSelect
|
||||
clearable={true}
|
||||
data={availableSubreddits}
|
||||
placeholder="Filter by subreddits"
|
||||
pt="md"
|
||||
searchable={true}
|
||||
size="sm"
|
||||
value={selectedSubreddits}
|
||||
w="100%"
|
||||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -92,6 +92,9 @@ export const projectRouter = router({
|
|||
)
|
||||
.query(async ({ input: { projectSlug } }) => {
|
||||
return await prisma.project.findUnique({
|
||||
include: {
|
||||
subredditKeywords: true,
|
||||
},
|
||||
where: {
|
||||
slug: projectSlug,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ export const socialPostsRouter = router({
|
|||
z.object({
|
||||
cursor: z.string().nullish(),
|
||||
filter: z.object({
|
||||
subreddits: z.array(z.string()).optional(),
|
||||
tab: z.enum(['ALL', 'PENDING', 'REPLIED', 'IRRELEVANT']),
|
||||
}),
|
||||
pagination: z.object({
|
||||
|
|
@ -143,7 +144,7 @@ export const socialPostsRouter = router({
|
|||
.query(async ({ input }) => {
|
||||
const { cursor, filter, pagination, projectSlug } = input;
|
||||
const { limit } = pagination;
|
||||
const { tab } = filter;
|
||||
const { subreddits, tab } = filter;
|
||||
|
||||
let postFilter = {};
|
||||
|
||||
|
|
@ -223,6 +224,11 @@ export const socialPostsRouter = router({
|
|||
where: {
|
||||
...postFilter,
|
||||
projectId: project.id,
|
||||
...(subreddits && subreddits.length > 0 && {
|
||||
subreddit: {
|
||||
in: subreddits,
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -94,7 +94,6 @@ export type QueriedRedditPost = Omit<
|
|||
| 'postId'
|
||||
| 'projectId'
|
||||
| 'response'
|
||||
| 'response'
|
||||
| 'updatedAt'
|
||||
> & {
|
||||
reply: RedditPostReply | null;
|
||||
|
|
|
|||
Loading…
Reference in New Issue