update viewContent page

This commit is contained in:
root 2025-10-11 22:12:38 +00:00
parent 33e0349a1b
commit 997fbb6985
1 changed files with 131 additions and 96 deletions

View File

@ -5,7 +5,7 @@ import { useWebApp } from '@vkruglikov/react-telegram-web-app';
import { Button } from '~/shared/ui/button'; import { Button } from '~/shared/ui/button';
import { usePurchaseContent, useViewContent } from '~/shared/services/content'; import { usePurchaseContent, useViewContent } from '~/shared/services/content';
import { fromNanoTON } from '~/shared/utils'; import { fromNanoTON } from '~/shared/utils';
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { AudioPlayer } from '~/shared/ui/audio-player'; import { AudioPlayer } from '~/shared/ui/audio-player';
import { useAuth } from '~/shared/services/auth'; import { useAuth } from '~/shared/services/auth';
import { CongratsModal } from './components/congrats-modal'; import { CongratsModal } from './components/congrats-modal';
@ -39,16 +39,16 @@ export const ViewContentPage = () => {
const statusState = content?.data?.status?.state ?? "uploaded"; const statusState = content?.data?.status?.state ?? "uploaded";
const conversionState = content?.data?.conversion?.state; const conversionState = content?.data?.conversion?.state;
const uploadState = content?.data?.upload?.state; const uploadState = content?.data?.upload?.state;
const statusLabel = useMemo(() => { const statusMessage = useMemo(() => {
switch (statusState) { switch (statusState) {
case "ready":
return "Готово";
case "processing": case "processing":
return "Обрабатывается"; return "Контент обрабатывается";
case "failed": case "failed":
return "Ошибка"; return "Ошибка обработки";
case "ready":
return null;
default: default:
return "Загружено"; return "Файл загружен";
} }
}, [statusState]); }, [statusState]);
@ -56,6 +56,16 @@ export const ViewContentPage = () => {
const isAudio = Boolean(content?.data?.content_type?.startsWith('audio')); const isAudio = Boolean(content?.data?.content_type?.startsWith('audio'));
const isReady = Boolean(mediaUrl); const isReady = Boolean(mediaUrl);
const metadataName = content?.data?.display_options?.metadata?.name; const metadataName = content?.data?.display_options?.metadata?.name;
const contentTitle = metadataName || content?.data?.encrypted?.title || 'Контент';
const processingDetails = useMemo(() => {
if (!statusMessage) {
return null;
}
return {
conversion: conversionState,
upload: uploadState,
};
}, [conversionState, statusMessage, uploadState]);
const handleBuyContentTON = useCallback(async () => { const handleBuyContentTON = useCallback(async () => {
if (!contentId) { if (!contentId) {
@ -175,13 +185,27 @@ export const ViewContentPage = () => {
} }
}, [content, refetchContent]); }, [content, refetchContent]);
const haveLicense = useMemo(() => { const haveLicense = useMemo(() => (
document.title = content?.data?.display_options?.metadata?.name; content?.data?.have_licenses?.includes('listen') ||
return ( content?.data?.have_licenses?.includes('resale')
content?.data?.have_licenses?.includes('listen') || ), [content]);
content?.data?.have_licenses?.includes('resale')
); const hadLicenseRef = useRef<boolean>(haveLicense);
}, [content]);
useEffect(() => {
if (haveLicense && !hadLicenseRef.current) {
hadLicenseRef.current = true;
void refetchContent();
} else if (!haveLicense) {
hadLicenseRef.current = false;
}
}, [haveLicense, refetchContent]);
useEffect(() => {
if (contentTitle) {
document.title = contentTitle;
}
}, [contentTitle]);
useEffect(() => { useEffect(() => {
if (!contentId) { if (!contentId) {
@ -221,7 +245,7 @@ export const ViewContentPage = () => {
<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-[50px] px-4 '}>
{isCongratsModal && <CongratsModal onConfirm={handleConfirmCongrats} />} {isCongratsModal && <CongratsModal onConfirm={handleConfirmCongrats} />}
{isErrorModal && <ErrorModal onConfirm={handleErrorModal} />} {isErrorModal && <ErrorModal onConfirm={handleErrorModal} />}
{isAudio && {isReady && isAudio &&
content?.data?.display_options?.metadata?.image && ( content?.data?.display_options?.metadata?.image && (
<div className={'mt-[30px] h-[314px] w-full'}> <div className={'mt-[30px] h-[314px] w-full'}>
<img <img
@ -233,93 +257,104 @@ export const ViewContentPage = () => {
)} )}
{isReady ? ( {isReady ? (
isAudio ? ( <>
<AudioPlayer src={mediaUrl ?? ''} /> {isAudio ? (
) : ( <AudioPlayer src={mediaUrl ?? ''} />
<ReactPlayer ) : (
playsinline={true} <ReactPlayer
controls={true} playsinline={true}
width="100%" controls={true}
config={{ width="100%"
file: { config={{
attributes: { file: {
playsInline: true, attributes: {
autoPlay: true, playsInline: true,
poster: content?.data?.display_options?.metadata?.image || undefined, autoPlay: true,
poster: content?.data?.display_options?.metadata?.image || undefined,
},
}, },
}, }}
}} url={mediaUrl}
url={mediaUrl}
/>
)
) : (
<div className={'rounded-xl bg-slate-900/60 p-4 text-center text-sm text-slate-200'}>
<p>{statusLabel}. Конвертация может занять несколько минут.</p>
{conversionState && (
<p className={'mt-2 text-xs text-slate-400'}>
Статус конвертера: {conversionState}
</p>
)}
{uploadState && (
<p className={'mt-1 text-xs text-slate-400'}>
Загрузка: {uploadState}
</p>
)}
</div>
)}
<section className={'flex flex-col'}>
<h1 className={'text-[20px] font-bold'}>{metadataName}</h1>
<span className={'mt-1 text-xs text-slate-400'}>{statusLabel}</span>
{/*<h2>Russian</h2>*/}
{/*<h2>2022</h2>*/}
<p className={'mt-2 text-[12px]'}>
{content?.data?.display_options?.metadata?.description}
</p>
</section>
<div className="mt-auto pb-2">
{content?.data?.downloadable && (
<Button
onClick={() => handleDwnldContent()}
className={'h-[48px] mb-4'}
label={`Скачать контент`}
/>
)}
{!haveLicense && (
<div className="flex flex-row gap-4">
<Button
onClick={handleBuyContentTON}
className={'mb-4 h-[48px] px-2'}
label={`Купить за ${fromNanoTON(content?.data?.encrypted?.license?.resale?.price)} ТОН`}
includeArrows={content?.data?.invoice ? false : true}
/> />
{content?.data?.invoice && ( )}
<section className={'flex flex-col'}>
<h1 className={'text-[20px] font-bold'}>{metadataName}</h1>
<p className={'mt-2 text-[12px]'}>
{content?.data?.display_options?.metadata?.description}
</p>
</section>
<div className="mt-auto pb-2">
{content?.data?.downloadable && (
<Button <Button
onClick={handleBuyContentStars} onClick={() => handleDwnldContent()}
className={'mb-4 h-[48px] px-2'} className={'h-[48px] mb-4'}
label={`Купить за ${content?.data?.invoice?.amount} ⭐️`} label={`Скачать контент`}
/>
)}
{!haveLicense && (
<div className="flex flex-row gap-4">
<Button
onClick={handleBuyContentTON}
className={'mb-4 h-[48px] px-2'}
label={`Купить за ${fromNanoTON(content?.data?.encrypted?.license?.resale?.price)} ТОН`}
includeArrows={content?.data?.invoice ? false : true}
/>
{content?.data?.invoice && (
<Button
onClick={handleBuyContentStars}
className={'mb-4 h-[48px] px-2'}
label={`Купить за ${content?.data?.invoice?.amount} ⭐️`}
/>
)}
</div>
)}
<Button
onClick={() => {
WebApp.openTelegramLink(`https://t.me/MY_UploaderRobot`);
}}
className={'h-[48px] bg-darkred'}
label={`Загрузить свой контент`}
/>
{tonConnectUI.connected && (
<Button
onClick={() => {
tonConnectUI.disconnect();
}}
className={'h-[48px] bg-darkred mt-4'}
label={`Отключить кошелек`}
/> />
)} )}
</div> </div>
)} </>
<Button ) : (
onClick={() => { <div className="flex flex-1 flex-col items-center justify-center py-16">
WebApp.openTelegramLink(`https://t.me/MY_UploaderRobot`); <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">
className={'h-[48px] bg-darkred'} Контент скоро будет здесь
label={`Загрузить свой контент`} </h1>
/> <p className="mt-3 text-sm text-slate-300">
{tonConnectUI.connected && ( Мы уже обрабатываем загруженный файл и обновим страницу автоматически, как только появится доступ к полному контенту.
<Button </p>
onClick={() => { {statusMessage && (
tonConnectUI.disconnect(); <p className="mt-4 text-[12px] text-slate-500">
}} Текущее состояние: {statusMessage}
className={'h-[48px] bg-darkred mt-4'} </p>
label={`Отключить кошелек`} )}
/> {processingDetails?.conversion && (
)} <p className="mt-2 text-[12px] text-slate-500">
</div> Статус конвертера: {processingDetails.conversion}
</p>
)}
{processingDetails?.upload && (
<p className="mt-2 text-[12px] text-slate-500">
Загрузка: {processingDetails.upload}
</p>
)}
</div>
</div>
)}
</main> </main>
); );
}; };