Skip to content

Commit dd621f2

Browse files
fix: feed image thumbnails + mobile layout regressions (#1332)
* fix(feed): repair broken article thumbnails from doubled image URLs HackerNoon's RSS media:thumbnail/media:content URLs are malformed at the source — an already-absolute CDN URL prefixed with their own origin (https://hackernoon.com/https://cdn.hackernoon.com/…), which 404s. Our ingestion stored them verbatim, and the redesigned cards SSR the <img>, so the broken-image icon stuck: the error event fires before React hydrates and attaches onError, so the fallback never runs. - add unwrapDoubledUrl() (utils/url.ts) + unit tests - card: unwrap at render and detect pre-hydration failures via a ref callback, so a dead image collapses to no thumbnail (not a broken icon) - sanitise URLs at ingestion (fetch-rss, admin/sync-feeds — media + OG) - one-off scrub script for already-stored rows Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(mobile): resolve overflow, top-bar, filters and focus on small screens The relaunch redesign had several mobile regressions: - cards overflowed the viewport (byline meta line couldn't shrink) — now truncates; card padding p-4 on mobile - app-main side gutters reduced to 0.75rem and per-page content wrappers drop px-4 py-8 → px-0 py-4 on mobile (parent already gutters), so text gets full reading width without double padding - top bar collapses to burger + logo + search icon ≤720px; Log in / Join free move into the nav drawer; real search SVG replaces the tiny glyph - feed filters drop to their own row below the tabs instead of cramming under them, and no longer sit in an overflow container that clipped the FilterPill dropdowns (they now open on mobile) - command palette: Esc hint becomes an X close button on mobile - search field active state is a subtle accent underline, not the global (blue-resolving) focus ring Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(feed): anchor unwrapDoubledUrl to the host boundary Code review caught that the naive lastIndexOf scan would truncate a URL whose path legitimately contains http://https:// deeper down (path-based image proxies, or a slug literally containing a scheme). Match only an absolute URL sitting immediately after the host — the exact shape of the upstream doubled-origin bug — and leave everything else untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 4678f5a commit dd621f2

25 files changed

Lines changed: 328 additions & 46 deletions

File tree

app/(app)/[username]/[slug]/_userLinkDetail.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ const UserLinkDetail = ({ username, contentSlug, initialContent }: Props) => {
119119

120120
if (status === "pending") {
121121
return (
122-
<div className="mx-auto max-w-prose px-4 py-8">
122+
<div className="mx-auto max-w-prose px-0 py-4 sm:px-4 sm:py-8">
123123
<div className="animate-pulse">
124124
<div className="mb-4 h-6 w-24 rounded bg-elevated" />
125125
<div className="mb-4 h-4 w-48 rounded bg-elevated" />
@@ -134,7 +134,7 @@ const UserLinkDetail = ({ username, contentSlug, initialContent }: Props) => {
134134

135135
if (status === "error" || !linkContent) {
136136
return (
137-
<div className="mx-auto max-w-prose px-4 py-8">
137+
<div className="mx-auto max-w-prose px-0 py-4 sm:px-4 sm:py-8">
138138
<Link
139139
href="/"
140140
className="mb-6 inline-flex items-center gap-1.5 font-mono text-sm text-muted transition-colors hover:text-fg"
@@ -173,7 +173,7 @@ const UserLinkDetail = ({ username, contentSlug, initialContent }: Props) => {
173173
const isOwner = session?.user?.id === linkContent.author?.id;
174174

175175
return (
176-
<article className="mx-auto max-w-prose px-4 py-8">
176+
<article className="mx-auto max-w-prose px-0 py-4 sm:px-4 sm:py-8">
177177
<Link
178178
href="/"
179179
className="mb-6 inline-flex items-center gap-1.5 font-mono text-sm text-muted transition-colors hover:text-fg"

app/(app)/admin/_client.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ const AdminDashboard = () => {
7777
const { data: reportCounts } = api.report.getCounts.useQuery();
7878

7979
return (
80-
<div className="mx-auto max-w-6xl px-4 py-8">
80+
<div className="mx-auto max-w-6xl px-0 py-4 sm:px-4 sm:py-8">
8181
<div className="mb-8">
8282
<p className="eyebrow">
8383
<span className="slash">{"// "}</span>admin

app/(app)/admin/moderation/_client.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ const ModerationQueue = () => {
182182
highlightedItem === id ? "ring-2 ring-accent rounded-lg" : "";
183183

184184
return (
185-
<div className="mx-auto max-w-6xl px-4 py-8">
185+
<div className="mx-auto max-w-6xl px-0 py-4 sm:px-4 sm:py-8">
186186
<div className="mb-6 flex items-center gap-4">
187187
<Link
188188
href="/admin"

app/(app)/admin/sources/_client.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ const AdminSourcesPage = () => {
327327
};
328328

329329
return (
330-
<div className="mx-auto max-w-6xl px-4 py-8">
330+
<div className="mx-auto max-w-6xl px-0 py-4 sm:px-4 sm:py-8">
331331
<div className="mb-8 flex items-center justify-between">
332332
<div>
333333
<p className="eyebrow">

app/(app)/admin/tags/_client.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ const TagsAdmin = () => {
208208
};
209209

210210
return (
211-
<div className="mx-auto max-w-6xl px-4 py-8">
211+
<div className="mx-auto max-w-6xl px-0 py-4 sm:px-4 sm:py-8">
212212
<div className="mb-8 flex items-center justify-between">
213213
<div>
214214
<p className="eyebrow">

app/(app)/admin/users/_client.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ const UserManagement = () => {
8181
: usersData?.users;
8282

8383
return (
84-
<div className="mx-auto max-w-6xl px-4 py-8">
84+
<div className="mx-auto max-w-6xl px-0 py-4 sm:px-4 sm:py-8">
8585
<div className="mb-6 flex items-center gap-4">
8686
<Link
8787
href="/admin"

app/(app)/company/[slug]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export default async function Page(props: Props) {
1919
if (!company) return notFound();
2020

2121
return (
22-
<div className="mx-auto w-full max-w-4xl px-4 py-8 sm:px-6 lg:px-8">
22+
<div className="mx-auto w-full max-w-4xl px-0 py-4 sm:px-6 sm:py-8 lg:px-8">
2323
<div className="overflow-hidden rounded-lg bg-white shadow dark:bg-neutral-800">
2424
<div className="border-b border-neutral-200 p-6 dark:border-neutral-700">
2525
<div className="flex flex-col items-center gap-6 sm:flex-row">

app/(app)/draft/[id]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const PreviewPage = async (props: Props) => {
4848
const content = Markdoc.transform(ast, config);
4949

5050
return (
51-
<div className="mx-auto max-w-3xl px-4 py-8">
51+
<div className="mx-auto max-w-3xl px-0 py-4 sm:px-4 sm:py-8">
5252
<nav className="mb-6 flex items-center gap-2 text-sm">
5353
<span className="rounded-full bg-accent/15 px-3 py-1 font-medium text-accent">
5454
Draft Preview

app/(app)/feed/_client.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,12 @@ const FeedPage = ({ initialFeed }: { initialFeed?: FeedFirstPage | null }) => {
162162
{t.label}
163163
</button>
164164
))}
165-
<div className="ml-auto pl-4">{filterCluster}</div>
165+
{/* Mobile: filters drop to their own full-width row below the tabs
166+
instead of cramming under them. Desktop: inline, pushed right.
167+
No overflow clip here — it would hide the FilterPill popovers. */}
168+
<div className="order-last flex w-full justify-end pt-1 min-[640px]:order-none min-[640px]:ml-auto min-[640px]:w-auto min-[640px]:pl-4 min-[640px]:pt-0">
169+
{filterCluster}
170+
</div>
166171
</div>
167172
) : (
168173
<div className="flex items-center border-b border-hairline pb-2">

app/(app)/s/[sourceSlug]/[slug]/_feedArticleContent.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const FeedArticleContent = ({ sourceSlug, article }: Props) => {
4242
const safeExternalUrl = safeExternalHref(article.externalUrl);
4343

4444
return (
45-
<article className="mx-auto max-w-prose px-4 py-8">
45+
<article className="mx-auto max-w-prose px-0 py-4 sm:px-4 sm:py-8">
4646
<Link
4747
href="/"
4848
className="mb-6 inline-flex items-center gap-1.5 font-mono text-sm text-muted transition-colors hover:text-fg"

0 commit comments

Comments
 (0)