Правки профиля, работа с ссылками

This commit is contained in:
Халимов Рустам
2026-03-14 22:35:33 +03:00
parent c31a531a6a
commit be0a50b702
6 changed files with 369 additions and 132 deletions
+99
View File
@@ -0,0 +1,99 @@
import { useState, useEffect } from 'react';
interface LinkPreviewProps {
url: string;
}
interface MicrolinkData {
publisher?: string;
title?: string;
description?: string;
image?: { url: string };
logo?: { url: string };
}
export default function LinkPreview({ url }: LinkPreviewProps) {
const [data, setData] = useState<MicrolinkData | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let isMounted = true;
setLoading(true);
// Check cache first to avoid rate limiting
const cacheKey = `link_preview_${url}`;
const cached = sessionStorage.getItem(cacheKey);
if (cached) {
try {
const parsed = JSON.parse(cached);
if (isMounted) {
setData(parsed);
setLoading(false);
}
return;
} catch (e) {}
}
fetch(`https://api.microlink.io?url=${encodeURIComponent(url)}`)
.then(res => res.json())
.then(res => {
if (isMounted && res.status === 'success' && res.data) {
setData(res.data);
sessionStorage.setItem(cacheKey, JSON.stringify(res.data));
}
if (isMounted) setLoading(false);
})
.catch((err) => {
console.error('Failed to fetch link preview:', err);
if (isMounted) setLoading(false);
});
return () => {
isMounted = false;
};
}, [url]);
if (loading) {
return (
<div className="mt-2 text-xs text-vortex-400 opacity-70 italic border-l-[3px] border-vortex-500/50 pl-2">
Загрузка предпросмотра...
</div>
);
}
if (!data || (!data.title && !data.description && !data.image)) {
return null;
}
// Domain for publisher fallback
let domain = data.publisher;
if (!domain) {
try {
domain = new URL(url).hostname.replace(/^www\./, '');
} catch (e) {}
}
return (
<a
href={url}
target="_blank"
rel="noopener noreferrer"
className="block mt-2 border-l-[3px] border-vortex-500 bg-black/20 rounded-r-lg overflow-hidden hover:bg-black/30 transition-colors"
onClick={(e) => e.stopPropagation()}
>
<div className="p-2.5 flex flex-col gap-1.5">
<div className="flex items-center gap-1.5 text-xs font-semibold text-vortex-400">
{data.logo?.url && <img src={data.logo.url} alt="" className="w-3.5 h-3.5 rounded-sm object-cover" />}
<span className="truncate">{domain}</span>
</div>
{data.title && <div className="text-sm font-bold text-white leading-tight break-words">{data.title}</div>}
{data.description && <div className="text-[13px] text-zinc-300 line-clamp-3 leading-snug">{data.description}</div>}
</div>
{data.image?.url && (
<div className="w-full relative overflow-hidden bg-black/20" style={{ maxHeight: '300px' }}>
<img src={data.image.url} alt="" className="w-full h-full object-cover" />
</div>
)}
</a>
);
}