Conversation
* feat: update my feeds tab on profile page * feat: update tabs to be routes * fix: double scrollbar * comment unused files in overview tab * fix: revert rsbuild * fmt * fix: add routegen to prettier ignore * clean up, server side --------- Co-authored-by: Elliot Braem <elliot@ejlbraem.com>
* feat: add name and tag validation to create * feat: add no activity data in activity tab * add coming soon sections for profile page * feat: clicking on leaderboard feed hashtag will redirect to that feed * fix: keeps name on start when disable feed names collapse * fix: rsbuild * fix: add routegen to prettier ignore * fix: add ability to navigate to collapsed feeds in leaderboard * add ability to expand or collapse all * fix: rsbuild * adjustments * nitpicks --------- Co-authored-by: Elliot Braem <elliot@ejlbraem.com>
* feat: update the feed editor * feat: improve performance, and fix bugs * feat: revert local development change * add routegen to pretttier ignore * fix: resolve code rabbit comments * fix some nitpick comments * fix prettier and build config * formats * merge --------- Co-authored-by: Elliot Braem <elliot@ejlbraem.com>
* Feat: add recent feeds * feat: add recent content to the main feed page * fmt * Update apps/app/src/hooks/use-rss-feed.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * nitpicks --------- Co-authored-by: Elliot Braem <elliot@everything.dev> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Elliot Braem <elliot@ejlbraem.com>
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
|
""" WalkthroughThis update restructures the Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant DistributorBadges
participant distributor-urls.ts
User->>DistributorBadges: Render with distribute: DistributorConfig[]
DistributorBadges->>distributor-urls.ts: getDistributorDisplayName(distributor)
DistributorBadges->>distributor-urls.ts: getDistributorUrl(distributor)
alt URL exists
DistributorBadges->>User: Render clickable badge (anchor)
else No URL
DistributorBadges->>User: Render static badge (span)
end
sequenceDiagram
participant FeedCreationStore
participant setValues
participant RSS Distributor
FeedCreationStore->>setValues: setValues({ id, ... })
setValues->>RSS Distributor: Check if RSS distributor exists
alt Not found
setValues->>RSS Distributor: Create default RSS distributor config
end
setValues->>RSS Distributor: Update feedConfig title, description, serviceUrl
setValues->>RSS Distributor: Ensure feed id in categories mapping
Possibly related PRs
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 3
🔭 Outside diff range comments (6)
apps/app/src/routes/_layout/create/feed/_tabs.tsx (1)
12-16: Update step navigation paths to match new route structure.The step navigation still references the old route paths without the
_tabslayer. This could cause navigation issues.const steps = [ - { id: "/_layout/create/feed/", title: "Basic Information" }, - { id: "/_layout/create/feed/settings", title: "Curation Settings" }, - { id: "/_layout/create/feed/review", title: "Feed Review" }, + { id: "/_layout/create/feed/_tabs/", title: "Basic Information" }, + { id: "/_layout/create/feed/_tabs/settings", title: "Curation Settings" }, + { id: "/_layout/create/feed/_tabs/review", title: "Feed Review" }, ];apps/app/src/routes/_layout/create/feed/_tabs/review.tsx (1)
148-148: Update navigation path to match new route structure.The navigation to the settings page still uses the old route path without the
_tabslayer.- onClick={() => navigate({ to: "/create/feed/settings" })} + onClick={() => navigate({ to: "/create/feed/_tabs/settings" })}apps/app/src/routes/_layout/create/feed/_tabs/index.tsx (1)
63-63: Update navigation path to match new route structure.The navigation to the settings page still uses the old route path without the
_tabslayer.- to: "/create/feed/settings", + to: "/create/feed/_tabs/settings",apps/app/src/routes/_layout/create/feed/_tabs/settings.tsx (2)
68-68: Update navigation path to match new route structure.The navigation to the review page still uses the old route path without the
_tabslayer.- navigate({ to: "/create/feed/review" }); + navigate({ to: "/create/feed/_tabs/review" });
89-91: Update navigation path to match new route structure.The navigation to the feed creation page still uses the old route path without the
_tabslayer.navigate({ - to: "/create/feed", + to: "/create/feed/_tabs", })apps/app/src/store/feed-creation-store.ts (1)
1-200: Fix Prettier formatting issuesThe pipeline indicates formatting issues. Run
prettier --write apps/app/src/store/feed-creation-store.tsto fix the code style.
🧹 Nitpick comments (3)
apps/app/src/lib/distributor-urls.ts (1)
16-18: Consider more robust template variable detection.The current implementation only checks for
{{which might miss other template formats or cause false positives with legitimate URLs containing these characters.Consider a more specific regex pattern:
function isTemplateVariable(value: unknown): boolean { - return typeof value === "string" && value.includes("{{"); + return typeof value === "string" && /\{\{[^}]+\}\}/.test(value); }apps/app/src/store/feed-creation-store.ts (1)
95-152: Consider making RSS distributor creation explicit rather than automaticThe current implementation automatically creates and manages an RSS distributor whenever
setValuesis called with anid. This side effect might be unexpected for consumers of this store. Consider:
- Making RSS distributor creation an explicit action
- Adding a configuration option to control this behavior
- At minimum, documenting this automatic behavior clearly
apps/app/src/components/DistributorBadges.tsx (1)
19-47: Consider extracting Badge component to reduce duplicationThe badge styling and content are duplicated between the linked and non-linked versions. Consider extracting a reusable component.
+const DistributorBadge = ({ displayName, className }: { displayName: string; className?: string }) => ( + <Badge + className={`text-black border border-stone-500 rounded-md bg-stone-50 shadow-none px-2 py-0.5 text-xs whitespace-nowrap flex-shrink-0 ${className || ''}`} + > + {displayName} + </Badge> +); export function DistributorBadges({ distribute, }: { distribute: DistributorConfig[]; }) { return ( <div className="flex flex-wrap gap-1 max-w-full overflow-hidden"> {distribute.map((distributor, index) => { const displayName = getDistributorDisplayName(distributor); const url = getDistributorUrl(distributor); + const key = `${distributor.plugin}-${index}`; - const badgeContent = ( - <Badge className="text-black border border-stone-500 rounded-md bg-stone-50 shadow-none px-2 py-0.5 text-xs whitespace-nowrap flex-shrink-0 cursor-pointer hover:bg-stone-100 transition-colors"> - {displayName} - </Badge> - ); - - // If we have a URL, wrap in a link, otherwise just render the badge if (url) { return ( <a - key={`${distributor.plugin}-${index}`} + key={key} href={url} target="_blank" rel="noopener noreferrer" className="inline-block" > - {badgeContent} + <DistributorBadge + displayName={displayName} + className="cursor-pointer hover:bg-stone-100 transition-colors" + /> </a> ); } - return ( - <Badge - key={`${distributor.plugin}-${index}`} - className="text-black border border-stone-500 rounded-md bg-stone-50 shadow-none px-2 py-0.5 text-xs whitespace-nowrap flex-shrink-0" - > - {displayName} - </Badge> - ); + return <DistributorBadge key={key} displayName={displayName} />; })} </div> ); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
apps/app/src/components/DistributorBadges.tsx(1 hunks)apps/app/src/lib/distributor-urls.ts(1 hunks)apps/app/src/routeTree.gen.ts(21 hunks)apps/app/src/routes/_layout/create/feed/_tabs.tsx(1 hunks)apps/app/src/routes/_layout/create/feed/_tabs/index.tsx(1 hunks)apps/app/src/routes/_layout/create/feed/_tabs/review.tsx(1 hunks)apps/app/src/routes/_layout/create/feed/_tabs/settings.tsx(1 hunks)apps/app/src/store/feed-creation-store.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (5)
apps/app/src/routes/_layout/create/feed/_tabs.tsx (3)
apps/app/src/routes/_layout/create/feed/_tabs/index.tsx (1)
Route(37-40)apps/app/src/routes/_layout/create/feed/_tabs/review.tsx (1)
Route(9-11)apps/app/src/routes/_layout/create/feed/_tabs/settings.tsx (1)
Route(54-56)
apps/app/src/routes/_layout/create/feed/_tabs/review.tsx (3)
apps/app/src/routes/_layout/create/feed/_tabs.tsx (1)
Route(5-7)apps/app/src/routes/_layout/create/feed/_tabs/index.tsx (1)
Route(37-40)apps/app/src/routes/_layout/create/feed/_tabs/settings.tsx (1)
Route(54-56)
apps/app/src/routes/_layout/create/feed/_tabs/index.tsx (3)
apps/app/src/routes/_layout/create/feed/_tabs.tsx (1)
Route(5-7)apps/app/src/routes/_layout/create/feed/_tabs/review.tsx (1)
Route(9-11)apps/app/src/routes/_layout/create/feed/_tabs/settings.tsx (1)
Route(54-56)
apps/app/src/routes/_layout/create/feed/_tabs/settings.tsx (3)
apps/app/src/routes/_layout/create/feed/_tabs.tsx (1)
Route(5-7)apps/app/src/routes/_layout/create/feed/_tabs/index.tsx (1)
Route(37-40)apps/app/src/routes/_layout/create/feed/_tabs/review.tsx (1)
Route(9-11)
apps/app/src/components/DistributorBadges.tsx (2)
apps/app/src/lib/distributor-urls.ts (3)
DistributorConfig(8-11)getDistributorDisplayName(195-211)getDistributorUrl(148-190)packages/types/src/config.ts (1)
DistributorConfig(101-101)
🪛 GitHub Actions: CI
apps/app/src/store/feed-creation-store.ts
[error] 1-1: Prettier formatting check failed. Run 'prettier --write' to fix code style issues.
🔇 Additional comments (9)
apps/app/src/lib/distributor-urls.ts (3)
148-190: LGTM! Well-structured distributor URL routing.The main URL generation function handles multiple distributor types with appropriate fallbacks. The switch statement is comprehensive and the plugin name normalization is well-implemented.
195-211: LGTM! Consistent display name formatting.The display name function properly handles special cases like "NEAR Social" and "RSS" while providing sensible defaults with proper capitalization.
216-261: LGTM! Comprehensive specific URL validation.The
hasSpecificUrlfunction correctly checks for meaningful configuration values across all supported distributors, enabling proper conditional rendering logic.apps/app/src/routes/_layout/create/feed/_tabs.tsx (1)
5-7: LGTM! Route path updated correctly.The route path has been properly updated to include the new
_tabsintermediate layer, consistent with the route restructuring.apps/app/src/routes/_layout/create/feed/_tabs/review.tsx (1)
9-11: LGTM! Route path updated correctly.The route path has been properly updated to include the new
_tabsintermediate layer.apps/app/src/routes/_layout/create/feed/_tabs/index.tsx (1)
37-40: LGTM! Route path updated correctly.The route path has been properly updated to include the new
_tabsintermediate layer.apps/app/src/routes/_layout/create/feed/_tabs/settings.tsx (1)
54-56: LGTM! Route path updated correctly.The route path has been properly updated to include the new
_tabsintermediate layer.apps/app/src/components/DistributorBadges.tsx (1)
1-51: Good refactoring to use centralized utility functionsThe refactoring to use
getDistributorDisplayNameandgetDistributorUrlutilities improves consistency and maintainability across the application.apps/app/src/routeTree.gen.ts (1)
1-908: Auto-generated file - no manual changes neededThis file is automatically generated by TanStack Router and reflects the route restructuring changes. The new
_tabsintermediate layer is correctly represented in the route tree.
| const match = url.match(/https:\/\/([^.]+)\.supabase\.co/); | ||
| if (match) { | ||
| const projectRef = match[1]; | ||
| return `https://supabase.com/dashboard/project/${projectRef}`; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Strengthen URL validation for Supabase project extraction.
The regex pattern for extracting Supabase project references could be more robust and should handle edge cases.
- const match = url.match(/https:\/\/([^.]+)\.supabase\.co/);
+ const match = url.match(/^https:\/\/([a-zA-Z0-9-]+)\.supabase\.co(?:\/|$)/);This ensures:
- URL starts with https://
- Project ref contains only valid characters
- Properly handles trailing slashes
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const match = url.match(/https:\/\/([^.]+)\.supabase\.co/); | |
| if (match) { | |
| const projectRef = match[1]; | |
| return `https://supabase.com/dashboard/project/${projectRef}`; | |
| } | |
| const match = url.match(/^https:\/\/([a-zA-Z0-9-]+)\.supabase\.co(?:\/|$)/); | |
| if (match) { | |
| const projectRef = match[1]; | |
| return `https://supabase.com/dashboard/project/${projectRef}`; | |
| } |
🤖 Prompt for AI Agents
In apps/app/src/lib/distributor-urls.ts around lines 129 to 133, the current
regex for extracting the Supabase project reference is too loose and may match
invalid URLs. Update the regex to ensure the URL starts with https://, the
project reference contains only valid characters (e.g., alphanumeric and
hyphens), and it properly handles optional trailing slashes. This will make the
URL validation more robust and prevent incorrect matches.
| if (channelId) { | ||
| // Discord channel URLs format: https://discord.com/channels/serverId/channelId | ||
| // Since we don't have serverId, we use @me which works for direct channel links | ||
| return `https://discord.com/channels/@me/${channelId}`; |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Verify Discord channel URL format compatibility.
The Discord URL format using @me may not work for all channel types. Server channels typically require the actual server ID.
Please verify that Discord channel URLs with @me work correctly for your use case:
🌐 Web query:
Does Discord support channel URLs in the format https://discord.com/channels/@me/channelId for all channel types?
💡 Result:
Yes, Discord supports channel URLs in the format https://discord.com/channels/@me/channelId for direct message (DM) channels. This URL structure allows users to access their private conversations directly. For other channel types, such as text or voice channels within a server (guild), the URL format is https://discord.com/channels/guildId/channelId. This distinction ensures that users can navigate to both server-based and direct message channels using appropriate URLs.
Ensure correct Discord channel URL generation for DMs and guild channels
We verified that https://discord.com/channels/@me/<channelId> only works for direct‐message channels, whereas server (guild) channels require the guild ID in the URL. If this utility may be used for guild channels, update it to accept an optional guildId and generate the URL accordingly.
• apps/app/src/lib/distributor-urls.ts: adjust return value
• Introduce an optional guildId parameter (or pull it from context)
Suggested diff:
- return `https://discord.com/channels/@me/${channelId}`;
+ if (guildId) {
+ return `https://discord.com/channels/${guildId}/${channelId}`;
+ }
+ return `https://discord.com/channels/@me/${channelId}`;Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/app/src/lib/distributor-urls.ts at line 104, the function currently
returns a URL formatted only for direct-message channels using
`https://discord.com/channels/@me/${channelId}`. To support both DM and guild
channels, modify the function to accept an optional `guildId` parameter. If
`guildId` is provided, return the URL as
`https://discord.com/channels/${guildId}/${channelId}`, otherwise keep the
existing DM URL format. Update the function signature and the return statement
accordingly to handle both cases.
| if (values.id && state.feedConfig.outputs?.stream?.distribute) { | ||
| let rssDistributor = state.feedConfig.outputs.stream.distribute.find( | ||
| (d) => d.plugin === "@curatedotfun/rss", | ||
| ); | ||
| if (!rssDistributor) { | ||
| rssDistributor = { | ||
| plugin: "@curatedotfun/rss", | ||
| config: { | ||
| apiSecret: "{{RSS_SERVICE_API_SECRET}}", | ||
| feedConfig: { | ||
| title: "", | ||
| description: "", | ||
| }, | ||
| serviceUrl: "", | ||
| }, | ||
| transform: [ | ||
| { | ||
| plugin: "@curatedotfun/object-transform", | ||
| config: { | ||
| mappings: { | ||
| link: "{{source}}", | ||
| title: "{{title}}", | ||
| author: { | ||
| link: "https://x.com/{{author}}", | ||
| name: "{{username}}", | ||
| }, | ||
| source: { | ||
| url: "{{source}}", | ||
| title: "twitter", | ||
| }, | ||
| content: "<h2>{{title}}</h2><p>{{summary}}</p>", | ||
| categories: ["{{tags}}"], | ||
| description: "{{summary}}", | ||
| publishedAt: "{{createdAt}}", | ||
| }, | ||
| }, | ||
| }, | ||
| ], | ||
| }; | ||
| state.feedConfig.outputs.stream.distribute.push(rssDistributor); | ||
| } | ||
| rssDistributor.config.feedConfig.title = values.name ?? ""; | ||
| rssDistributor.config.feedConfig.description = | ||
| values.description ?? ""; | ||
| rssDistributor.config.serviceUrl = `https://rss.curate.fun/${values.id}/rss.xml`; | ||
| const objectTransform = rssDistributor.transform?.find( | ||
| (t) => t.plugin === "@curatedotfun/object-transform", | ||
| ); | ||
| if ( | ||
| objectTransform && | ||
| Array.isArray(objectTransform.config.mappings.categories) | ||
| ) { | ||
| const categories = objectTransform.config.mappings.categories; | ||
| if (!categories.includes(values.id)) { | ||
| categories.push(values.id); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Extract RSS distributor management logic into a helper function
The RSS distributor setup logic is complex and deeply nested within setValues. This reduces readability and makes the code harder to test and maintain.
Consider extracting this logic into a dedicated helper function:
+const createOrUpdateRssDistributor = (
+ feedId: string,
+ name: string,
+ description: string,
+ existingDistributors: DistributorConfig[] = []
+): DistributorConfig[] => {
+ const distributors = [...existingDistributors];
+ let rssDistributor = distributors.find(d => d.plugin === "@curatedotfun/rss");
+
+ if (!rssDistributor) {
+ rssDistributor = {
+ plugin: "@curatedotfun/rss",
+ config: {
+ apiSecret: "{{RSS_SERVICE_API_SECRET}}",
+ feedConfig: { title: "", description: "" },
+ serviceUrl: "",
+ },
+ transform: [
+ {
+ plugin: "@curatedotfun/object-transform",
+ config: {
+ mappings: {
+ link: "{{source}}",
+ title: "{{title}}",
+ author: {
+ link: "https://x.com/{{author}}",
+ name: "{{username}}",
+ },
+ source: {
+ url: "{{source}}",
+ title: "twitter",
+ },
+ content: "<h2>{{title}}</h2><p>{{summary}}</p>",
+ categories: ["{{tags}}"],
+ description: "{{summary}}",
+ publishedAt: "{{createdAt}}",
+ },
+ },
+ },
+ ],
+ };
+ distributors.push(rssDistributor);
+ }
+
+ // Update RSS distributor configuration
+ rssDistributor.config.feedConfig.title = name;
+ rssDistributor.config.feedConfig.description = description;
+ rssDistributor.config.serviceUrl = `https://rss.curate.fun/${feedId}/rss.xml`;
+
+ // Update categories
+ const objectTransform = rssDistributor.transform?.find(
+ t => t.plugin === "@curatedotfun/object-transform"
+ );
+ if (objectTransform && Array.isArray(objectTransform.config.mappings.categories)) {
+ const categories = objectTransform.config.mappings.categories;
+ if (!categories.includes(feedId)) {
+ categories.push(feedId);
+ }
+ }
+
+ return distributors;
+};
setValues: (values) => {
setState((state) => {
state.feedConfig = { ...state.feedConfig, ...values };
- if (values.id && state.feedConfig.outputs?.stream?.distribute) {
- let rssDistributor = state.feedConfig.outputs.stream.distribute.find(
- (d) => d.plugin === "@curatedotfun/rss",
- );
- // ... rest of the RSS logic
+ if (values.id && state.feedConfig.outputs?.stream) {
+ state.feedConfig.outputs.stream.distribute = createOrUpdateRssDistributor(
+ values.id,
+ values.name ?? "",
+ values.description ?? "",
+ state.feedConfig.outputs.stream.distribute
+ );
}
});
},🤖 Prompt for AI Agents
In apps/app/src/store/feed-creation-store.ts around lines 95 to 152, the RSS
distributor setup logic is deeply nested inside the setValues function, making
it hard to read and maintain. Extract this entire block that manages the
rssDistributor creation, configuration, and updating into a separate helper
function that accepts necessary parameters like state and values. Then call this
helper from setValues to keep the main function cleaner and improve testability.
Summary by CodeRabbit
New Features
Enhancements
Refactor
/create/feedroute structure was reorganized to introduce a nested_tabslayer, affecting the URLs for feed creation, review, and settings pages.