refactor(posts): return the card display domain from the backend

Compute the bare display host once in LinkCardMetadata (via Laravel's Uri::host + Str::chopStart) and return it as card.domain, so the LinkCard component renders it directly instead of parsing the URL client-side. The component is now purely presentational.
This commit is contained in:
Paulo Castellano 2026-07-17 16:24:23 -03:00
parent 7c79b331f6
commit 925fd85b95
4 changed files with 22 additions and 15 deletions

View file

@ -4,6 +4,9 @@
namespace App\Services\Social\LinkCard;
use Illuminate\Support\Str;
use Illuminate\Support\Uri;
final readonly class LinkCardMetadata
{
public function __construct(
@ -14,15 +17,29 @@ public function __construct(
) {}
/**
* @return array{uri: string, title: string, description: string, image: ?string}
* @return array{uri: string, domain: string, title: string, description: string, image: ?string}
*/
public function toArray(): array
{
return [
'uri' => $this->uri,
'domain' => $this->domain(),
'title' => $this->title,
'description' => $this->description,
'image' => $this->imageUrl,
];
}
/**
* The bare display host for the card (e.g. "nyt.com" from
* "https://www.nyt.com/x"), so the frontend renders it without parsing URLs.
*/
private function domain(): string
{
$host = Uri::of($this->uri)->host();
return $host === null || $host === ''
? $this->uri
: Str::chopStart($host, 'www.');
}
}

View file

@ -1,19 +1,7 @@
<script setup lang="ts">
import { computed } from 'vue';
import type { LinkCard } from '@/composables/useLinkCard';
const props = defineProps<{ card: LinkCard }>();
const domain = computed(() => {
try {
const { hostname } = new URL(props.card.uri);
return hostname.startsWith('www.') ? hostname.slice(4) : hostname;
} catch {
return props.card.uri;
}
});
defineProps<{ card: LinkCard }>();
</script>
<template>
@ -25,7 +13,7 @@ const domain = computed(() => {
class="aspect-[1.91/1] w-full object-cover"
/>
<div class="px-3 py-2">
<div class="text-[13px] text-neutral-500 dark:text-neutral-400">{{ domain }}</div>
<div class="text-[13px] text-neutral-500 dark:text-neutral-400">{{ card.domain }}</div>
<div
v-if="card.title"
class="mt-0.5 text-[15px] font-semibold text-neutral-900 dark:text-neutral-100 line-clamp-2"

View file

@ -7,6 +7,7 @@ import type { MediaItem } from '@/types/media';
export interface LinkCard {
uri: string;
domain: string;
title: string;
description: string;
image: string | null;

View file

@ -28,6 +28,7 @@
->assertOk()
->assertJson([
'uri' => 'https://example.com',
'domain' => 'example.com',
'title' => 'Example',
'description' => 'Desc',
'image' => 'https://example.com/card.png',