viewcontent fix

This commit is contained in:
root 2025-10-20 15:32:32 +00:00
parent 3b31c4e6cb
commit 61b50df864
1 changed files with 92 additions and 44 deletions

View File

@ -52,9 +52,22 @@ export const ViewContentPage = () => {
} }
}, [statusState]); }, [statusState]);
const haveLicense = useMemo(() => (
content?.data?.have_licenses?.includes('listen') ||
content?.data?.have_licenses?.includes('resale')
), [content]);
const contentMime = content?.data?.content_mime ?? null;
const contentKind = content?.data?.display_options?.content_kind ?? null;
const mediaUrl = content?.data?.display_options?.content_url ?? null; const mediaUrl = content?.data?.display_options?.content_url ?? null;
const isAudio = Boolean(content?.data?.content_type?.startsWith('audio')); const isAudio = contentKind === 'audio' || Boolean(content?.data?.content_type?.startsWith('audio'));
const isReady = Boolean(mediaUrl); const isVideo = contentKind === 'video' || Boolean(content?.data?.content_type?.startsWith('video'));
const isBinary = contentKind === 'binary' || (!isAudio && !isVideo);
const hasInlinePlayer = Boolean(mediaUrl) && (isAudio || isVideo);
const binaryDownloadReady = Boolean(mediaUrl) && isBinary;
const isReadyState = statusState === "ready";
const previewAvailable = Boolean(content?.data?.display_options?.has_preview);
const coverImage = content?.data?.display_options?.metadata?.image ?? null;
const metadataName = content?.data?.display_options?.metadata?.name; const metadataName = content?.data?.display_options?.metadata?.name;
const contentTitle = metadataName || content?.data?.encrypted?.title || 'Контент'; const contentTitle = metadataName || content?.data?.encrypted?.title || 'Контент';
const processingDetails = useMemo(() => { const processingDetails = useMemo(() => {
@ -67,6 +80,10 @@ export const ViewContentPage = () => {
}; };
}, [conversionState, statusMessage, uploadState]); }, [conversionState, statusMessage, uploadState]);
const canDownload = Boolean(mediaUrl) && haveLicense && ((content?.data?.downloadable ?? false) || isBinary);
const binaryAwaitingAccess = isBinary && !binaryDownloadReady;
const isFailed = statusState === "failed";
const handleBuyContentTON = useCallback(async () => { const handleBuyContentTON = useCallback(async () => {
if (!contentId) { if (!contentId) {
console.error('No content identifier available for purchase'); console.error('No content identifier available for purchase');
@ -185,11 +202,6 @@ export const ViewContentPage = () => {
} }
}, [content, refetchContent]); }, [content, refetchContent]);
const haveLicense = useMemo(() => (
content?.data?.have_licenses?.includes('listen') ||
content?.data?.have_licenses?.includes('resale')
), [content]);
const hadLicenseRef = useRef<boolean>(haveLicense); const hadLicenseRef = useRef<boolean>(haveLicense);
useEffect(() => { useEffect(() => {
@ -230,11 +242,15 @@ export const ViewContentPage = () => {
const handleDwnldContent = async () => { const handleDwnldContent = async () => {
try { try {
const fileUrl = content?.data?.display_options?.content_url; const fileUrl = content?.data?.display_options?.content_url;
if (!fileUrl) {
return;
}
const fileName = content?.data?.display_options?.metadata?.name || 'content'; const fileName = content?.data?.display_options?.metadata?.name || 'content';
const fileFormat = content?.data?.content_ext || '.raw'; const rawExt = content?.data?.content_ext ?? 'bin';
const normalizedExt = rawExt.replace(/^\.+/, '');
await WebApp.downloadFile({ await WebApp.downloadFile({
url: fileUrl, url: fileUrl,
file_name: fileName + '.' + fileFormat, file_name: `${fileName}.${normalizedExt}`,
}); });
} catch (error) { } catch (error) {
console.error('Error downloading content:', error); console.error('Error downloading content:', error);
@ -242,23 +258,28 @@ export const ViewContentPage = () => {
}; };
return ( return (
<main className={'min-h-screen flex w-full flex-col gap-[50px] px-4 '}> <main className={'min-h-screen flex w-full flex-col gap-[40px] px-4 '}>
{isCongratsModal && <CongratsModal onConfirm={handleConfirmCongrats} />} {isCongratsModal && <CongratsModal onConfirm={handleConfirmCongrats} />}
{isErrorModal && <ErrorModal onConfirm={handleErrorModal} />} {isErrorModal && <ErrorModal onConfirm={handleErrorModal} />}
{isReady && isAudio && {coverImage && (
content?.data?.display_options?.metadata?.image && ( <div className="mt-[30px] flex w-full justify-center">
<div className={'mt-[30px] h-[314px] w-full'}> <div className="relative aspect-square w-full max-w-[320px] rounded-3xl border border-slate-900/60 bg-transparent">
<div className="absolute inset-0 flex items-center justify-center overflow-hidden">
<img <img
alt={'content_image'} alt={'content cover'}
className={'h-full w-full object-cover object-center'} className={'max-h-full max-w-full object-contain'}
src={content?.data?.display_options?.metadata?.image} src={coverImage}
loading="lazy"
/> />
</div> </div>
</div>
</div>
)} )}
{isReady ? ( {isReadyState ? (
<> <>
{isAudio ? ( {hasInlinePlayer && (
isAudio ? (
<AudioPlayer src={mediaUrl ?? ''} /> <AudioPlayer src={mediaUrl ?? ''} />
) : ( ) : (
<ReactPlayer <ReactPlayer
@ -274,8 +295,19 @@ export const ViewContentPage = () => {
}, },
}, },
}} }}
url={mediaUrl} url={mediaUrl ?? ''}
light={coverImage || undefined}
/> />
)
)}
{binaryDownloadReady && (
<div className="rounded-2xl border border-slate-800 bg-slate-950/70 px-6 py-5 shadow-inner shadow-black/20">
<h2 className="text-base font-semibold text-slate-100">Файл готов к скачиванию</h2>
<p className="mt-2 text-sm text-slate-300">
{contentMime ? `Тип файла: ${contentMime}` : 'Скачайте оригинальные данные на устройство.'}
</p>
</div>
)} )}
<section className={'flex flex-col'}> <section className={'flex flex-col'}>
@ -285,26 +317,40 @@ export const ViewContentPage = () => {
</p> </p>
</section> </section>
{binaryAwaitingAccess && (
<div className="rounded-2xl border border-slate-800 bg-slate-950/70 px-6 py-5 text-sm text-slate-300">
<h2 className="text-base font-semibold text-slate-100">Предпросмотр недоступен</h2>
<p className="mt-2">
Этот файл нельзя открыть в браузере. Получите доступ, чтобы скачать оригинал на устройство.
</p>
{!previewAvailable && (
<p className="mt-2 text-xs text-slate-500">
Предпросмотр не формируется для бинарных данных.
</p>
)}
</div>
)}
<div className="mt-auto pb-2"> <div className="mt-auto pb-2">
{content?.data?.downloadable && ( {canDownload && (
<Button <Button
onClick={() => handleDwnldContent()} onClick={() => handleDwnldContent()}
className={'h-[48px] mb-4'} className={'h-[48px] mb-4'}
label={`Скачать контент`} label={`Скачать ${isBinary ? 'файл' : 'контент'}`}
/> />
)} )}
{!haveLicense && ( {!haveLicense && (
<div className="flex flex-row gap-4"> <div className="flex gap-4 pb-2">
<Button <Button
onClick={handleBuyContentTON} onClick={handleBuyContentTON}
className={'mb-4 h-[48px] px-2'} className={'mb-4 h-[48px] px-2 flex-1 min-w-[140px] w-auto'}
label={`Купить за ${fromNanoTON(content?.data?.encrypted?.license?.resale?.price)} ТОН`} label={`Купить за ${fromNanoTON(content?.data?.encrypted?.license?.resale?.price)} ТОН`}
includeArrows={content?.data?.invoice ? false : true} includeArrows={content?.data?.invoice ? false : true}
/> />
{content?.data?.invoice && ( {content?.data?.invoice && (
<Button <Button
onClick={handleBuyContentStars} onClick={handleBuyContentStars}
className={'mb-4 h-[48px] px-2'} className={'mb-4 h-[48px] px-2 flex-1 min-w-[140px] w-auto'}
label={`Купить за ${content?.data?.invoice?.amount} ⭐️`} label={`Купить за ${content?.data?.invoice?.amount} ⭐️`}
/> />
)} )}
@ -332,10 +378,12 @@ export const ViewContentPage = () => {
<div className="flex flex-1 flex-col items-center justify-center py-16"> <div className="flex flex-1 flex-col items-center justify-center py-16">
<div className="max-w-md rounded-2xl border border-slate-800 bg-slate-950/70 px-6 py-8 text-center shadow-lg shadow-black/30"> <div className="max-w-md rounded-2xl border border-slate-800 bg-slate-950/70 px-6 py-8 text-center shadow-lg shadow-black/30">
<h1 className="text-lg font-semibold text-slate-100"> <h1 className="text-lg font-semibold text-slate-100">
Контент скоро будет здесь {isFailed ? 'Не удалось подготовить контент' : 'Контент скоро будет здесь'}
</h1> </h1>
<p className="mt-3 text-sm text-slate-300"> <p className="mt-3 text-sm text-slate-300">
Мы уже обрабатываем загруженный файл и обновим страницу автоматически, как только появится доступ к полному контенту. {isFailed
? 'При обработке файла произошла ошибка. Попробуйте обновить страницу или повторно загрузить контент.'
: 'Мы уже обрабатываем загруженный файл и обновим страницу автоматически, как только появится доступ к полному контенту.'}
</p> </p>
{statusMessage && ( {statusMessage && (
<p className="mt-4 text-[12px] text-slate-500"> <p className="mt-4 text-[12px] text-slate-500">