diff --git a/server/src/render/reddit.rs b/server/src/render/reddit.rs index 431f7075bf0a281cbc30fdc4ff985c257ab34c8b..6e3ff524c72cb0d708a51d608263365845c09186 100644 --- a/server/src/render/reddit.rs +++ b/server/src/render/reddit.rs @@ -28,6 +28,7 @@ pub fn entity_markup(node: &NodeState, nsfw_ok: bool) -> Option { node_is_nsfw(node), nsfw_ok, &node.id.browse_href(), + &node.id.to_browse_url(), )) } @@ -66,7 +67,13 @@ pub fn child_row_markup(tree: &GlobalTree, id: &ItemId, href: &str, nsfw_ok: boo }) } -fn post_entity_card(data: &EntityData, is_nsfw: bool, nsfw_ok: bool, return_to: &str) -> Markup { +fn post_entity_card( + data: &EntityData, + is_nsfw: bool, + nsfw_ok: bool, + return_to: &str, + item_url: &str, +) -> Markup { let image = data.image_url.as_ref().or(data.thumb_url.as_ref()); let gated = is_nsfw && !nsfw_ok; if gated { @@ -84,14 +91,16 @@ fn post_entity_card(data: &EntityData, is_nsfw: bool, nsfw_ok: bool, return_to: " adult Reddit content" } } - @if let Some(url) = &data.link_url { + @if !item_url.is_empty() { p class="reddit-post-url muted small" { - a href=(url) rel="noopener noreferrer" { (url) } + a href=(item_url) target="_blank" rel="noopener noreferrer" { (item_url) } } } @if let Some(src) = image { figure class="reddit-post-figure" { - img class="reddit-post-image" src=(src) alt="" loading="lazy"; + a class="reddit-post-image-link" href=(item_url) target="_blank" rel="noopener noreferrer" { + img class="reddit-post-image" src=(src) alt="" loading="lazy"; + } } } @if let Some(body) = &data.body_html { @@ -142,7 +151,19 @@ mod tests { let html = entity_markup(&nsfw_node(), true).unwrap().into_string(); assert!(html.contains("adult body")); assert!(html.contains("https://example.com/image.jpg")); - assert!(html.contains("https://example.com/out")); + assert!( + html.contains("https://reddit.com/r/nsfw/comments/abc"), + "link and image should use the item URL: {html}" + ); + assert!( + html.contains("target=\"_blank\""), + "item links must open in a new tab: {html}" + ); + assert!( + html.contains("reddit-post-image-link"), + "image should be wrapped in a link: {html}" + ); + assert!(!html.contains("https://example.com/out")); assert!(!html.contains("nsfw-gate")); } diff --git a/server/static/sorter.css b/server/static/sorter.css index 82be6b6200e1b6d9268fa042511525c68c66f028..ec15ebc23ec1581764adf62da50d087a6042ab03 100644 --- a/server/static/sorter.css +++ b/server/static/sorter.css @@ -429,7 +429,7 @@ code { height: 64px; object-fit: cover; border-radius: 0; - border: 1px solid var(--border); + border: none; } .reddit-post-url a { @@ -440,12 +440,19 @@ code { margin: 0.75rem 0; } +.reddit-post-image-link { + display: block; + line-height: 0; + color: inherit; + text-decoration: none; +} + .reddit-post-image { display: block; max-width: 100%; height: auto; border-radius: 0; - border: 1px solid var(--border); + border: none; } .scope-name { @@ -933,7 +940,9 @@ h1 { @media (max-width: 640px) { .scope-theme { + --page-inline: 0.75rem; padding: 0.75rem; + padding-inline: var(--page-inline); padding-bottom: 0.5rem; overflow-x: clip; } @@ -967,7 +976,11 @@ h1 { .vote-compare-pair[data-winner="even"] { grid-template-columns: 1fr 1fr; - gap: 0.5rem; + /* Flush to screen edges; no gap so halves meet. */ + gap: 0; + margin-left: calc(-1 * var(--page-inline)); + margin-right: calc(-1 * var(--page-inline)); + width: calc(100% + 2 * var(--page-inline)); } .vote-compare-pair[data-winner="even"] .vote-compare-side { @@ -977,6 +990,7 @@ h1 { .vote-compare-pair[data-winner="even"] .entity-card h2 { font-size: 0.95rem; line-height: 1.25; + padding-inline: 0.5rem; } .vote-compare-pair[data-winner="even"] .entity-body, @@ -985,6 +999,14 @@ h1 { display: none; } + .vote-compare-pair[data-winner="even"] .reddit-post-figure { + /* Parent pair is already full-bleed; don't double 100vw. */ + width: 100%; + max-width: 100%; + margin-left: 0; + margin-right: 0; + } + .vote-compare-pair[data-winner="even"] .reddit-post-image { max-height: 9rem; width: 100%; @@ -992,7 +1014,9 @@ h1 { } .vote-compare-pair[data-winner="even"] .entity-section { - padding: 0.65rem; + padding: 0; + border-left: none; + border-right: none; } .vote-compare-side { @@ -1001,13 +1025,25 @@ h1 { .vote-compare-side .entity-section { overflow-wrap: anywhere; + overflow: visible; + } + + /* Full-bleed: every image pixel touches the phone screen edges. */ + .reddit-post-figure { + width: 100vw; + max-width: 100vw; + margin-left: calc(50% - 50vw); + margin-right: calc(50% - 50vw); } .reddit-post-image { - max-height: min(55vh, 22rem); width: 100%; - object-fit: contain; - background: color-mix(in oklch, var(--panel) 70%, black); + max-width: none; + height: auto; + max-height: none; + object-fit: unset; + background: transparent; + border: none; } .vote-hud-inner {