Skip to content

Improve meta and style#665

Merged
feruzm merged 5 commits into
developfrom
meta
Feb 19, 2026
Merged

Improve meta and style#665
feruzm merged 5 commits into
developfrom
meta

Conversation

@feruzm
Copy link
Copy Markdown
Member

@feruzm feruzm commented Feb 19, 2026

Summary by CodeRabbit

  • New Features

    • Richer metadata across site, feeds and profiles (Open Graph + Twitter cards) and conditional RSS alternates.
    • Centralized site metadata export for consistent defaults.
    • Consolidated wallet operations into a single modal with optional external control.
  • Bug Fixes

    • Prevented text overflow in profile properties.
    • Improved responsive layout for entry controls and tooltip sizing on small screens.
  • Style

    • Updated Open Graph/Twitter image references.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 19, 2026

📝 Walkthrough

Walkthrough

Adds a root-level Next.js metadata export and expands Open Graph/Twitter metadata across community, feed, and profile generators; centralizes meta image usage and alternates (canonical/RSS); updates homepage OG/Twitter image; converts WalletOperationsDialog to a controlled mode and centralizes dialog rendering; minor layout and tooltip style tweaks.

Changes

Cohort / File(s) Summary
Root layout & site metadata
apps/web/src/app/layout.tsx
Adds exported metadata: Metadata (uses defaults.json) with site-level Open Graph and Twitter settings.
Community / Feed / Profile metadata
apps/web/src/app/(dynamicPages)/community/[community]/_helpers/generate-community-metadata.ts, apps/web/src/app/(dynamicPages)/feed/[...sections]/_helpers/generate-feed-metadata.ts, apps/web/src/app/(dynamicPages)/profile/[username]/_helpers/generate-profile-metadata.ts
Centralizes meta image variable usage; adds/extends openGraph and twitter blocks; adds alternates including canonical and conditional RSS entries; keeps function signatures.
Homepage OG/Twitter image
apps/web/src/app/page.tsx
Replaces OG/Twitter image URL to base/og.jpg (was defaults.imageServer path).
Wallet dialog control flow
apps/web/src/features/wallet/wallet-operations-dialog.tsx, apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/profile-wallet-tokens-list-item.tsx
Adds controlled?: { show, onHide } to WalletOperationsDialog (controlled visibility + reset effect); lifts active operation state into profile tokens list item and renders a single dialog instance.
Profile card overflow / truncation
apps/web/src/app/(dynamicPages)/profile/[username]/_components/profile-card/profile-card-extra-property.tsx
Adds layout constraints (min-w-0, shrink-0) and wraps children in a .truncate container to prevent overflow.
Entry list responsive layout
apps/web/src/features/shared/entry-list-item/index.tsx
Changes bottom control bar classes to w-full flex by default and md:inline-flex on medium screens (responsive width behavior).
Tooltip styling adjustment
apps/web/src/styles/_mixins.scss
In compact_vote_slider tooltip rule for small screens: width changed to 93vw, left: auto, and max-width: none.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant TokenList as ProfileTokensListItem
    participant Dialog as WalletOperationsDialog

    rect rgba(200,220,255,0.5)
    User->>TokenList: open actions dropdown & select operation
    end

    rect rgba(200,255,200,0.5)
    TokenList->>TokenList: setActiveOperation(operation)
    TokenList->>Dialog: render with controlled.show = true
    end

    rect rgba(255,220,200,0.5)
    Dialog->>User: display modal (initialized/reset)
    User->>Dialog: complete or cancel
    Dialog->>TokenList: call controlled.onHide()
    TokenList->>Dialog: setActiveOperation(null) (dialog hidden)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐇 I tidy metadata, align each card and feed,
One dialog now listens when tokens ask to lead.
Images hop in line, RSS peeks in the lane,
Truncated text curls snug, tooltips fit again.
A little rabbit cheers — neat changes in the plain.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Improve meta and style' is vague and generic, using non-descriptive terms that don't clearly convey the specific changes in the PR. Use a more specific title that highlights the main change, such as 'Add metadata configuration and fix responsive styles' or 'Enhance SEO metadata and improve layout constraints'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch meta

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/web/src/app/(dynamicPages)/profile/[username]/_helpers/generate-profile-metadata.ts (1)

25-41: ⚠️ Potential issue | 🟠 Major

Missing alternates.canonical — potential SEO regression.

metaUrl is computed and used only for openGraph.url (a relative path). The alternates.canonical field that signals the authoritative URL to search engines is absent from the return object. If the previous implementation set a canonical URL (the AI summary notes that metaCanonical was removed), this is a regression that can lead to duplicate-content indexing across /posts, /comments, /replies, etc. sections of the same profile.

🔧 Suggested addition
     const metaUrl = `/@${username.replace("@", "")}${section ? `/${section}` : ""}`;
+    const metaCanonical = `${base}${metaUrl}`;
     const metaImage = `${defaults.imageServer}/u/${username.replace("@", "")}/avatar/medium`;
     const metaKeywords = ...;
     return {
       title: metaTitle,
       description: metaDescription,
+      alternates: {
+        canonical: metaCanonical,
+      },
       openGraph: { ... },
       twitter: { ... },
     };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/web/src/app/`(dynamicPages)/profile/[username]/_helpers/generate-profile-metadata.ts
around lines 25 - 41, The returned metadata object from
generate-profile-metadata is missing alternates.canonical which causes an SEO
regression; update the return value to include an alternates object with
canonical set to the authoritative URL (use the existing metaCanonical if
present, otherwise build an absolute URL from metaUrl), e.g. add alternates: {
canonical: metaCanonical || metaUrlAbsolute } alongside the existing openGraph
and twitter fields so search engines get the canonical URL; reference the same
symbols used in the diff (metaUrl, metaTitle, metaDescription, metaImage,
metaKeywords) when adding alternates.canonical.
apps/web/src/app/(dynamicPages)/feed/[...sections]/_helpers/generate-feed-metadata.ts (1)

11-41: ⚠️ Potential issue | 🟠 Major

canonical and rss are dead code; their absence from the return is a potential SEO/discovery regression.

canonical and rss are computed with conditional logic across two branches (lines 11–25) but are not present in the returned object (lines 28–41). The branch-aware computation strongly suggests these were previously included in an alternates block. Omitting them means:

  • Feed pages have no canonical URL, risking duplicate-content indexing across filter/tag permutations.
  • RSS feeds (e.g., /trending/bitcoin/rss.xml) are not advertised in <head>, breaking feed-reader auto-discovery.
🔧 Suggested fix
   return {
     title,
     description,
+    alternates: {
+      canonical,
+      ...(rss ? { types: { "application/rss+xml": rss } } : {}),
+    },
     openGraph: {
       title,
       description,
       url,
     },
     twitter: {
       card: "summary",
       title,
       description,
     },
   };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/web/src/app/`(dynamicPages)/feed/[...sections]/_helpers/generate-feed-metadata.ts
around lines 11 - 41, The computed canonical and rss variables in
generate-feed-metadata.ts are not returned and thus dropped; update the returned
metadata object to include the branch-aware canonical URL and advertise the RSS
feed (use the existing canonical and rss variables) — e.g., add a top-level
canonical property set to canonical and include the RSS under alternates (or
alternates.types / alternates['application/rss+xml']) so feed readers and search
engines receive the correct canonical URL and feed auto-discovery.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/web/src/app/`(dynamicPages)/community/[community]/_helpers/generate-community-metadata.ts:
- Around line 18-36: metaRss and metaCanonical are computed but never used, so
add them back into the metadata return by populating alternates.canonical with
metaCanonical and exposing the RSS link (e.g., under
alternates.types["application/rss+xml"] or a links/rss field) so the generated
object returned from this helper includes the canonical URL and RSS discovery;
update the return in the function that currently builds
title/description/openGraph/twitter (referencing metaRss, metaCanonical,
metaImage, and the metadata return object) to include alternates: { canonical:
metaCanonical, types: { "application/rss+xml": metaRss } } (or the existing app
convention for RSS links) so the values are no longer dead code.

---

Outside diff comments:
In
`@apps/web/src/app/`(dynamicPages)/feed/[...sections]/_helpers/generate-feed-metadata.ts:
- Around line 11-41: The computed canonical and rss variables in
generate-feed-metadata.ts are not returned and thus dropped; update the returned
metadata object to include the branch-aware canonical URL and advertise the RSS
feed (use the existing canonical and rss variables) — e.g., add a top-level
canonical property set to canonical and include the RSS under alternates (or
alternates.types / alternates['application/rss+xml']) so feed readers and search
engines receive the correct canonical URL and feed auto-discovery.

In
`@apps/web/src/app/`(dynamicPages)/profile/[username]/_helpers/generate-profile-metadata.ts:
- Around line 25-41: The returned metadata object from generate-profile-metadata
is missing alternates.canonical which causes an SEO regression; update the
return value to include an alternates object with canonical set to the
authoritative URL (use the existing metaCanonical if present, otherwise build an
absolute URL from metaUrl), e.g. add alternates: { canonical: metaCanonical ||
metaUrlAbsolute } alongside the existing openGraph and twitter fields so search
engines get the canonical URL; reference the same symbols used in the diff
(metaUrl, metaTitle, metaDescription, metaImage, metaKeywords) when adding
alternates.canonical.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (6)
apps/web/src/features/wallet/wallet-operations-dialog.tsx (2)

52-55: Controlled setShow(true) is silently a no-op.

When in controlled mode, calling setShow(true) does nothing. Currently no code path calls setShow(true) in controlled mode (the trigger wrapper is hidden), so this is safe. But the asymmetry is worth a brief inline comment for future maintainers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/features/wallet/wallet-operations-dialog.tsx` around lines 52 -
55, The controlled-mode setter currently only invokes controlled.onHide() when
given false, so setShow in controlled mode is a no-op for true; add a brief
inline comment next to the setShow definition (near symbols show, setShow,
controlled, internalShow, setInternalShow, controlled.onHide) stating that
setShow(true) is intentionally a no-op in controlled mode (no onShow is called)
because the trigger is hidden and visibility is driven externally, to prevent
future confusion for maintainers.

64-70: Missing initialData in the dependency array of the reset effect.

This effect calls setData(initialData) but omits initialData from its dependency list. If a future caller provides both controlled and initialData and the latter changes independently, the form could reset with a stale value. Current consumers don't pass initialData in controlled mode, so the risk is theoretical.

Suggested fix
   useEffect(() => {
     if (controlled?.show) {
       setStep("form");
       setData(initialData);
       setSignError(undefined);
     }
-  }, [controlled?.show]);
+  }, [controlled?.show, initialData]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/features/wallet/wallet-operations-dialog.tsx` around lines 64 -
70, The effect that resets form state (useEffect that checks controlled?.show
and calls setStep("form"), setData(initialData), setSignError(undefined)) is
missing initialData from its dependency array; update the dependency array to
include initialData (e.g., [controlled?.show, initialData]) so the reset uses
the latest initialData when it changes, ensuring setData(initialData) runs with
the current value.
apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/profile-wallet-tokens-list-item.tsx (1)

187-198: Consider adding a key to force remount on operation change.

WalletOperationsDialog is conditionally rendered only when activeOperation !== null, so it mounts fresh each time. However, if a code path ever sets activeOperation directly from one non-null value to another (without passing through null), the component would receive new operation/asset props without the controlled-show reset effect re-firing (since controlled.show remains true). Adding a key tied to the operation prevents stale form state in that edge case.

Suggested fix
   const activeOperationDialog = activeOperation !== null ? (
     <WalletOperationsDialog
+      key={activeOperation}
       controlled={{ show: true, onHide: () => setActiveOperation(null) }}
       asset={assetSymbol}
       operation={activeOperation}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/web/src/app/`(dynamicPages)/profile/[username]/wallet/_components/profile-wallet-tokens-list-item.tsx
around lines 187 - 198, The WalletOperationsDialog can stay mounted if
activeOperation flips between non-null values, so add a key prop tied to the
changing identifiers to force a remount when operation changes; update the JSX
that renders WalletOperationsDialog (the expression using activeOperation,
controlled={{ show: true, onHide: () => setActiveOperation(null) }},
asset={assetSymbol}, operation={activeOperation}) to include a key derived from
activeOperation and assetSymbol (e.g. combine operation id/type and assetSymbol)
so the component fully remounts whenever the operation/asset changes.
apps/web/src/app/(dynamicPages)/feed/[...sections]/_helpers/generate-feed-metadata.ts (1)

40-44: Consider "summary_large_image" for richer social previews on tag/feed pages.

twitter.card: "summary" renders only a small thumbnail. For tag/topic feed pages that aggregate community content, "summary_large_image" produces a more prominent card — especially relevant if a default OG image is added to these pages in a follow-up.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/web/src/app/`(dynamicPages)/feed/[...sections]/_helpers/generate-feed-metadata.ts
around lines 40 - 44, Update the Twitter card type in the feed metadata to use a
large preview: in the twitter object inside generate-feed-metadata (the twitter
property where card is currently set to "summary"), change the card value to
"summary_large_image" so tag/topic feed pages render with a bigger image preview
(prepare to pair this with any default OG image you may add later).
apps/web/src/app/(dynamicPages)/profile/[username]/_helpers/generate-profile-metadata.ts (2)

28-33: RSS alternate is emitted unconditionally for all section values.

The application/rss+xml link is always added, even when section is "engine" (tokens), "communities", "replies", etc., where a posts-RSS feed is semantically irrelevant to the current page. Consider restricting this to sections that actually have a corresponding feed:

💡 Suggested fix
      alternates: {
        canonical: `${base}${metaUrl}`,
-       types: {
-         "application/rss+xml": `${base}/@${username.replace("@", "")}/rss`,
-       },
+       ...((!section || section === "posts" || section === "blog") && {
+         types: {
+           "application/rss+xml": `${base}/@${username.replace("@", "")}/rss`,
+         },
+       }),
      },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/web/src/app/`(dynamicPages)/profile/[username]/_helpers/generate-profile-metadata.ts
around lines 28 - 33, The alternates.types entry unconditionally adds an
"application/rss+xml" RSS alternate for every profile page; restrict this so RSS
is only emitted for sections that have a posts feed (e.g., the default/profile
posts view and any other list-of-posts sections) by checking the section
variable before adding the RSS type in generate-profile-metadata.ts: when
building alternates (the object that includes canonical and types), only set
types["application/rss+xml"] = `${base}/@${username.replace("@","")}/rss` if
section is one of the allowed sections (e.g., undefined or "posts" or other
post-list sections), otherwise omit the RSS key so pages like "engine",
"communities", "replies" don't get an RSS alternate.

24-24: Consider using string[] for openGraph.tags to emit separate keyword tags.

Line 24 defines metaKeywords as a single comma-separated string. While Next.js accepts string | string[] | null for openGraph.tags, passing a comma-separated string produces a single <meta property="og:article:tag"> element with the entire value, not two distinct tags. To emit separate keyword tags, use an array:

-    const metaKeywords = `${username.replace("@", "")}, ${username.replace("@", "")}'s blog`;
+    const metaKeywords = [
+      username.replace("@", ""),
+      `${username.replace("@", "")}'s blog`,
+    ];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/web/src/app/`(dynamicPages)/profile/[username]/_helpers/generate-profile-metadata.ts
at line 24, metaKeywords is built as a single comma-separated string which
causes openGraph.tags to output one combined tag; change metaKeywords to an
array of strings and pass that array to openGraph.tags instead. Specifically,
replace the single-string construction referenced by metaKeywords with an array
like [username.replace("@",""), `${username.replace("@","")}'s blog`] and ensure
the openGraph.tags property (where metaKeywords is used) receives this string[]
so Next.js emits separate <meta property="og:article:tag"> entries.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@apps/web/src/app/`(dynamicPages)/community/[community]/_helpers/generate-community-metadata.ts:
- Around line 21-42: No change required to behavior — the metadata now
consistently uses the metaImage constant in openGraph.images and twitter.images
and includes alternates.canonical and alternates.types["application/rss+xml"];
just ensure any remaining dead vars metaRss or metaCanonical are removed if
still declared and keep metaImage, openGraph, twitter, and alternates as shown
(references: metaImage, openGraph.images, twitter.images, alternates.canonical,
alternates.types["application/rss+xml"]).

---

Nitpick comments:
In
`@apps/web/src/app/`(dynamicPages)/feed/[...sections]/_helpers/generate-feed-metadata.ts:
- Around line 40-44: Update the Twitter card type in the feed metadata to use a
large preview: in the twitter object inside generate-feed-metadata (the twitter
property where card is currently set to "summary"), change the card value to
"summary_large_image" so tag/topic feed pages render with a bigger image preview
(prepare to pair this with any default OG image you may add later).

In
`@apps/web/src/app/`(dynamicPages)/profile/[username]/_helpers/generate-profile-metadata.ts:
- Around line 28-33: The alternates.types entry unconditionally adds an
"application/rss+xml" RSS alternate for every profile page; restrict this so RSS
is only emitted for sections that have a posts feed (e.g., the default/profile
posts view and any other list-of-posts sections) by checking the section
variable before adding the RSS type in generate-profile-metadata.ts: when
building alternates (the object that includes canonical and types), only set
types["application/rss+xml"] = `${base}/@${username.replace("@","")}/rss` if
section is one of the allowed sections (e.g., undefined or "posts" or other
post-list sections), otherwise omit the RSS key so pages like "engine",
"communities", "replies" don't get an RSS alternate.
- Line 24: metaKeywords is built as a single comma-separated string which causes
openGraph.tags to output one combined tag; change metaKeywords to an array of
strings and pass that array to openGraph.tags instead. Specifically, replace the
single-string construction referenced by metaKeywords with an array like
[username.replace("@",""), `${username.replace("@","")}'s blog`] and ensure the
openGraph.tags property (where metaKeywords is used) receives this string[] so
Next.js emits separate <meta property="og:article:tag"> entries.

In
`@apps/web/src/app/`(dynamicPages)/profile/[username]/wallet/_components/profile-wallet-tokens-list-item.tsx:
- Around line 187-198: The WalletOperationsDialog can stay mounted if
activeOperation flips between non-null values, so add a key prop tied to the
changing identifiers to force a remount when operation changes; update the JSX
that renders WalletOperationsDialog (the expression using activeOperation,
controlled={{ show: true, onHide: () => setActiveOperation(null) }},
asset={assetSymbol}, operation={activeOperation}) to include a key derived from
activeOperation and assetSymbol (e.g. combine operation id/type and assetSymbol)
so the component fully remounts whenever the operation/asset changes.

In `@apps/web/src/features/wallet/wallet-operations-dialog.tsx`:
- Around line 52-55: The controlled-mode setter currently only invokes
controlled.onHide() when given false, so setShow in controlled mode is a no-op
for true; add a brief inline comment next to the setShow definition (near
symbols show, setShow, controlled, internalShow, setInternalShow,
controlled.onHide) stating that setShow(true) is intentionally a no-op in
controlled mode (no onShow is called) because the trigger is hidden and
visibility is driven externally, to prevent future confusion for maintainers.
- Around line 64-70: The effect that resets form state (useEffect that checks
controlled?.show and calls setStep("form"), setData(initialData),
setSignError(undefined)) is missing initialData from its dependency array;
update the dependency array to include initialData (e.g., [controlled?.show,
initialData]) so the reset uses the latest initialData when it changes, ensuring
setData(initialData) runs with the current value.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
apps/web/src/app/(dynamicPages)/profile/[username]/_helpers/generate-profile-metadata.ts (2)

22-24: Extract cleanUsername to avoid four repeated replace("@", "") calls

username.replace("@", "") appears on lines 22, 23, 24, and 33. A single extracted variable improves readability and keeps the stripping logic in one place.

♻️ Proposed refactor
+    const cleanUsername = username.replace("@", "");
-    const metaUrl = `/@${username.replace("@", "")}${section ? `/${section}` : ""}`;
-    const metaImage = `${defaults.imageServer}/u/${username.replace("@", "")}/avatar/medium`;
-    const metaKeywords = [username.replace("@", ""), `${username.replace("@", "")}'s blog`];
+    const metaUrl = `/@${cleanUsername}${section ? `/${section}` : ""}`;
+    const metaImage = `${defaults.imageServer}/u/${cleanUsername}/avatar/medium`;
+    const metaKeywords = [cleanUsername, `${cleanUsername}'s blog`];
     const rsssections = ["posts", "blog", ""];
     return {
       ...
           types: {
-            "application/rss+xml": `${base}/@${username.replace("@", "")}/rss`,
+            "application/rss+xml": `${base}/@${cleanUsername}/rss`,
           },

Also applies to: 33-33

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/web/src/app/`(dynamicPages)/profile/[username]/_helpers/generate-profile-metadata.ts
around lines 22 - 24, Extract a single cleaned username variable (e.g.,
cleanUsername) in generate-profile-metadata so you don't call
username.replace("@", "") repeatedly; replace all occurrences used to build
metaUrl, metaImage, metaKeywords (and the use on line 33) with that variable to
centralize the stripping logic and improve readability.

25-25: undefined in rsssections is unreachable dead code

section is inferred as string from its default value "posts", so it can never be undefined at runtime. The undefined entry in the array is never matched and adds confusion.

♻️ Proposed fix
-    const rsssections = ["posts", "blog", undefined, ""];
+    const rsssections = ["posts", "blog", ""];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/web/src/app/`(dynamicPages)/profile/[username]/_helpers/generate-profile-metadata.ts
at line 25, The array constant rsssections contains an unreachable undefined
entry because the parameter/variable section is inferred as string with default
"posts"; remove the undefined element from rsssections (leave valid string
values like "posts", "blog", ""), and scan any comparisons against undefined in
the generate-profile-metadata logic to adjust them to check for empty string or
use explicit fallbacks; target the rsssections constant and any code referencing
it (e.g., the section variable handling) to ensure no runtime reliance on
undefined remains.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/web/src/app/`(dynamicPages)/profile/[username]/_helpers/generate-profile-metadata.ts:
- Around line 29-36: openGraph.url is currently set to the relative metaUrl
while alternates.canonical uses the absolute `${base}${metaUrl}`; update the
openGraph.url assignment (in the generate-profile-metadata function) to use the
same absolute URL by concatenating base and metaUrl (e.g., `${base}${metaUrl}`)
so social crawlers receive a fully-qualified og:url consistent with
alternates.canonical; keep existing username/section/rsssections logic
unchanged.

---

Nitpick comments:
In
`@apps/web/src/app/`(dynamicPages)/profile/[username]/_helpers/generate-profile-metadata.ts:
- Around line 22-24: Extract a single cleaned username variable (e.g.,
cleanUsername) in generate-profile-metadata so you don't call
username.replace("@", "") repeatedly; replace all occurrences used to build
metaUrl, metaImage, metaKeywords (and the use on line 33) with that variable to
centralize the stripping logic and improve readability.
- Line 25: The array constant rsssections contains an unreachable undefined
entry because the parameter/variable section is inferred as string with default
"posts"; remove the undefined element from rsssections (leave valid string
values like "posts", "blog", ""), and scan any comparisons against undefined in
the generate-profile-metadata logic to adjust them to check for empty string or
use explicit fallbacks; target the rsssections constant and any code referencing
it (e.g., the section variable handling) to ensure no runtime reliance on
undefined remains.

Comment on lines +29 to +36
alternates: {
canonical: `${base}${metaUrl}`,
...(rsssections.includes(section) && {
types: {
"application/rss+xml": `${base}/@${username.replace("@", "")}/rss`,
},
}),
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

openGraph.url should be an absolute URL, consistent with alternates.canonical

alternates.canonical is correctly built as ${base}${metaUrl} (absolute), but openGraph.url (line 41) is still the bare relative path (e.g. /@username/posts). Social-media crawlers expect og:url to be a fully-qualified absolute URL; a relative value may be silently dropped or misresolved, degrading link previews.

🐛 Proposed fix
       openGraph: {
         title: metaTitle,
         description: metaDescription,
         images: [metaImage],
-        url: metaUrl,
+        url: `${base}${metaUrl}`,
         tags: metaKeywords,
       },

Also applies to: 41-41

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/web/src/app/`(dynamicPages)/profile/[username]/_helpers/generate-profile-metadata.ts
around lines 29 - 36, openGraph.url is currently set to the relative metaUrl
while alternates.canonical uses the absolute `${base}${metaUrl}`; update the
openGraph.url assignment (in the generate-profile-metadata function) to use the
same absolute URL by concatenating base and metaUrl (e.g., `${base}${metaUrl}`)
so social crawlers receive a fully-qualified og:url consistent with
alternates.canonical; keep existing username/section/rsssections logic
unchanged.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
apps/web/src/app/(dynamicPages)/profile/[username]/_helpers/generate-profile-metadata.ts (1)

14-14: Use an anchored regex to strip the leading @ from username.

String.prototype.replace("@", string) removes only the first occurrence, anywhere in the string. If username somehow contains an embedded @ (e.g., from a malformed input), this silently corrupts cleanUsername. Using /^@/ makes the intent explicit and limits removal to the leading character.

♻️ Proposed fix
-    const cleanUsername = username.replace("@", "");
+    const cleanUsername = username.replace(/^@/, "");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/web/src/app/`(dynamicPages)/profile/[username]/_helpers/generate-profile-metadata.ts
at line 14, Replace the unanchored replace call that can remove any first "@"
with a leading-only removal: update the cleanUsername assignment (the
username.replace call used to compute cleanUsername inside
generate-profile-metadata) to use an anchored regex that only strips a leading
"@" (e.g., use /^@/), so embedded "@" characters in username are preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@apps/web/src/app/`(dynamicPages)/profile/[username]/_helpers/generate-profile-metadata.ts:
- Line 42: openGraph.url is being set to the relative metaUrl; update it to use
the absolute URL built from base and metaUrl so og:url is fully qualified (e.g.
use `${base}${metaUrl}`) — modify the openGraph.url assignment in
generate-profile-metadata.ts (the same place alternates.canonical uses
`${base}${metaUrl}`) to concatenate base and metaUrl so social crawlers receive
an absolute URL.

---

Nitpick comments:
In
`@apps/web/src/app/`(dynamicPages)/profile/[username]/_helpers/generate-profile-metadata.ts:
- Line 14: Replace the unanchored replace call that can remove any first "@"
with a leading-only removal: update the cleanUsername assignment (the
username.replace call used to compute cleanUsername inside
generate-profile-metadata) to use an anchored regex that only strips a leading
"@" (e.g., use /^@/), so embedded "@" characters in username are preserved.

@feruzm feruzm merged commit 22a6868 into develop Feb 19, 2026
1 check passed
@feruzm feruzm deleted the meta branch February 19, 2026 17:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant