Skip to content
3 changes: 2 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ node_modules
.turbo
.next
.docusaurus
packages/shared-db/migrations
packages/shared-db/migrations
apps/app/src/routeTree.gen.ts
16 changes: 15 additions & 1 deletion apps/api/src/routes/api/feeds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import {
FeedWrappedResponseSchema,
UpdateFeedRequestSchema,
} from "@curatedotfun/types";
import { ForbiddenError, NotFoundError } from "@curatedotfun/utils";
import {
ConflictError,
ForbiddenError,
NotFoundError,
} from "@curatedotfun/utils";
import { zValidator } from "@hono/zod-validator";
import { Hono } from "hono";
import { z } from "zod";
Expand Down Expand Up @@ -84,6 +88,16 @@ feedsRoutes.post(
);
} catch (error) {
c.var.sp.getLogger().error({ error, accountId }, "Error creating feed");
if (error instanceof ConflictError) {
return c.json(
ApiErrorResponseSchema.parse({
statusCode: 409,
success: false,
error: { message: error.message },
}),
409,
);
}
return c.json(
ApiErrorResponseSchema.parse({
statusCode: 500,
Expand Down
39 changes: 39 additions & 0 deletions apps/api/src/routes/api/users.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { FeedService } from "@curatedotfun/core-services";
import {
ApiErrorResponseSchema,
CreateUserRequestSchema,
FeedsWrappedResponseSchema,
UpdateUserRequestSchema,
UserDeletedWrappedResponseSchema,
UserNearAccountIdParamSchema,
Expand Down Expand Up @@ -333,4 +335,41 @@ usersRoutes.get(
},
);

usersRoutes.get(
"/:nearAccountId/feeds",
zValidator("param", UserNearAccountIdParamSchema),
async (c) => {
const { nearAccountId } = c.req.valid("param");
const sp = c.var.sp;

try {
const feedService: FeedService = sp.getFeedService();
const feeds = await feedService.getFeedsByCreator(nearAccountId);

return c.json(
FeedsWrappedResponseSchema.parse({
statusCode: 200,
success: true,
data: feeds.map((feed) => ({
...feed,
config: feed.config,
})),
}),
);
} catch (error) {
c.var.sp
.getLogger()
.error({ error }, `Error fetching feeds for ${nearAccountId}`);
return c.json(
ApiErrorResponseSchema.parse({
statusCode: 500,
success: false,
error: { message: "Failed to fetch feeds" },
}),
500,
);
}
},
);

export { usersRoutes };
165 changes: 0 additions & 165 deletions apps/app/src/components/BasicInformationForm.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion apps/app/src/components/UserMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export default function UserMenu({ className }: UserMenuProps) {
) : (
<ProfileImage size="small" />
)}
<p className="text-sm font-medium leading-6 hidden sm:block">
<p className="text-sm font-medium leading-6">
{getUserDisplayName()}
</p>
<ChevronDown
Expand Down
16 changes: 8 additions & 8 deletions apps/app/src/components/profile/ProfileTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import {
Activity,
ScanSearch,
// Award,
// Newspaper,
Newspaper,
// NotepadTextDashed
} from "lucide-react";
import { ProfileOverview } from "./overview";
// import { ProfileContent } from "./content";
// import { MyFeeds } from "./my-feeds";
import { MyFeeds } from "./my-feeds";
// import { ProfilePoints } from "./points";
import { ProfileActivity } from "./activity";

Expand All @@ -26,12 +26,12 @@ const TABS = [
// icon: NotepadTextDashed,
// component: ProfileContent,
// },
// {
// value: "my-feeds",
// label: "My Feeds",
// icon: Newspaper,
// component: MyFeeds,
// },
{
value: "my-feeds",
label: "My Feeds",
icon: Newspaper,
component: MyFeeds,
},
// {
// value: "points",
// label: "Points",
Expand Down
36 changes: 36 additions & 0 deletions apps/app/src/components/profile/my-feeds/FeedCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { FeedResponse } from "@curatedotfun/types";
import { useFeedStats } from "../../../lib/api/feeds";
import { Card } from "./card";

interface FeedCardProps {
feed: FeedResponse;
}

export function FeedCard({ feed }: FeedCardProps) {
const { contentCount, curatorCount } = useFeedStats(feed.id);

const isComplete = !!(
feed.config &&
feed.config.name &&
feed.config.description &&
feed.config.sources &&
feed.config.sources.length > 0
);
Comment on lines +12 to +18
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Simplify the completion check using optional chaining.

The static analysis tool correctly suggests using optional chaining for cleaner, more readable code.

Apply this diff to improve readability:

-  const isComplete = !!(
-    feed.config &&
-    feed.config.name &&
-    feed.config.description &&
-    feed.config.sources &&
-    feed.config.sources.length > 0
-  );
+  const isComplete = !!(
+    feed.config?.name &&
+    feed.config?.description &&
+    feed.config?.sources?.length > 0
+  );
🧰 Tools
🪛 Biome (1.9.4)

[error] 13-14: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🤖 Prompt for AI Agents
In apps/app/src/components/profile/my-feeds/FeedCard.tsx around lines 12 to 18,
simplify the isComplete check by replacing the multiple logical AND checks with
optional chaining. Use optional chaining to access feed.config and its
properties safely, and check if sources array has length greater than zero in a
cleaner, more readable way.


const tags: string[] = []; // TODO: Extract tags from feed sources or create a tags system
const imageSrc = feed.config?.image || "/images/feed-image.png";

return (
<Card
id={feed.id}
image={imageSrc}
title={feed.name}
tags={tags}
description={feed.description || "No description available"}
createdAt={new Date(feed.createdAt)}
curators={curatorCount}
contents={contentCount}
isCompleted={isComplete}
/>
);
}
24 changes: 24 additions & 0 deletions apps/app/src/components/profile/my-feeds/SearchForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Search } from "lucide-react";
import { Input } from "../../ui/input";

interface SearchFormProps {
searchTerm: string;
onSearchChange: (value: string) => void;
}

export function SearchForm({ searchTerm, onSearchChange }: SearchFormProps) {
return (
<form className="relative w-full md:w-fit">
<Input
placeholder="Search feeds..."
value={searchTerm}
onChange={(e) => onSearchChange(e.target.value)}
className="ps-9 sm:min-w-[300px] w-full"
/>
<Search
className="absolute left-2 top-1/2 -translate-y-1/2 text-black/50 size-5"
strokeWidth={1.5}
/>
</form>
);
}
Loading