Compare commits
1 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
d2e350bc28 |
|
|
@ -1,4 +1,4 @@
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
@ -17,12 +17,29 @@ import { HashtagInput } from "~/shared/ui/hashtag-input";
|
||||||
import { Replace } from "~/shared/ui/icons/replace";
|
import { Replace } from "~/shared/ui/icons/replace";
|
||||||
import { DisclaimerModal } from "./components/disclaimer-modal";
|
import { DisclaimerModal } from "./components/disclaimer-modal";
|
||||||
|
|
||||||
|
const formatFileSize = (bytes: number | undefined) => {
|
||||||
|
if (!bytes) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const units = ["Б", "КБ", "МБ", "ГБ"];
|
||||||
|
let value = bytes;
|
||||||
|
let unitIndex = 0;
|
||||||
|
while (value >= 1024 && unitIndex < units.length - 1) {
|
||||||
|
value /= 1024;
|
||||||
|
unitIndex += 1;
|
||||||
|
}
|
||||||
|
return `${value.toFixed(unitIndex === 0 ? 0 : 1)} ${units[unitIndex]}`;
|
||||||
|
};
|
||||||
|
|
||||||
type DataStepProps = {
|
type DataStepProps = {
|
||||||
nextStep(): void;
|
nextStep(): void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DataStep = ({ nextStep }: DataStepProps) => {
|
export const DataStep = ({ nextStep }: DataStepProps) => {
|
||||||
const rootStore = useRootStore();
|
const rootStore = useRootStore();
|
||||||
|
const isAudioFile = rootStore.fileType?.startsWith("audio");
|
||||||
|
const isVideoFile = rootStore.fileType?.startsWith("video");
|
||||||
|
const isMediaFile = isAudioFile || isVideoFile;
|
||||||
const [disclaimerAccepted, setDisclaimerAccepted] = useState(false);
|
const [disclaimerAccepted, setDisclaimerAccepted] = useState(false);
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -61,11 +78,13 @@ export const DataStep = ({ nextStep }: DataStepProps) => {
|
||||||
})();
|
})();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFileReset = () => {
|
const handleFileReset = useCallback(() => {
|
||||||
rootStore.setFile(null);
|
rootStore.setFile(null);
|
||||||
rootStore.setFileSrc('');
|
rootStore.setFileSrc('');
|
||||||
rootStore.setFileType('');
|
rootStore.setFileType('');
|
||||||
}
|
rootStore.setDownloadLocked(false);
|
||||||
|
rootStore.setAllowDwnld(false);
|
||||||
|
}, [rootStore]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -80,6 +99,39 @@ export const DataStep = ({ nextStep }: DataStepProps) => {
|
||||||
localStorage.setItem('disclaimerAccepted', 'true');
|
localStorage.setItem('disclaimerAccepted', 'true');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!rootStore.file) {
|
||||||
|
if (rootStore.isDownloadLocked) {
|
||||||
|
rootStore.setDownloadLocked(false);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isMediaFile) {
|
||||||
|
if (!rootStore.allowDwnld) {
|
||||||
|
rootStore.setAllowDwnld(true);
|
||||||
|
}
|
||||||
|
if (!rootStore.isDownloadLocked) {
|
||||||
|
rootStore.setDownloadLocked(true);
|
||||||
|
}
|
||||||
|
} else if (rootStore.isDownloadLocked) {
|
||||||
|
rootStore.setDownloadLocked(false);
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
isMediaFile,
|
||||||
|
rootStore,
|
||||||
|
rootStore.allowDwnld,
|
||||||
|
rootStore.file,
|
||||||
|
rootStore.isDownloadLocked,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const handleFileChange = useCallback((file: File) => {
|
||||||
|
rootStore.setFile(file);
|
||||||
|
rootStore.setFileSrc(URL.createObjectURL(file));
|
||||||
|
const mime = file.type || "application/octet-stream";
|
||||||
|
rootStore.setFileType(mime);
|
||||||
|
}, [rootStore]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={"mt-4 px-4 pb-8"}>
|
<section className={"mt-4 px-4 pb-8"}>
|
||||||
|
|
||||||
|
|
@ -118,20 +170,18 @@ export const DataStep = ({ nextStep }: DataStepProps) => {
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
|
|
||||||
<FormLabel label={"Файл"}>
|
<FormLabel label={"Файл"}>
|
||||||
{!rootStore.fileSrc && <>
|
{!rootStore.fileSrc && (
|
||||||
|
<>
|
||||||
<HiddenFileInput
|
<HiddenFileInput
|
||||||
id={"file"}
|
id={"file"}
|
||||||
shouldProcess={false}
|
shouldProcess={false}
|
||||||
accept={"video/mp4,video/x-m4v,video/*,audio/mp3,audio/*"}
|
accept={"*/*"}
|
||||||
onChange={(file) => {
|
onChange={handleFileChange}
|
||||||
rootStore.setFile(file);
|
|
||||||
rootStore.setFileSrc(URL.createObjectURL(file));
|
|
||||||
rootStore.setFileType(file.type); // Save the file type for conditional rendering
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FileButton htmlFor={"file"} />
|
<FileButton htmlFor={"file"} />
|
||||||
</>}
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{rootStore.fileSrc && (
|
{rootStore.fileSrc && (
|
||||||
<div
|
<div
|
||||||
|
|
@ -139,9 +189,10 @@ export const DataStep = ({ nextStep }: DataStepProps) => {
|
||||||
"w-full border border-white bg-[#2B2B2B] px-[10px] py-[8px] text-sm"
|
"w-full border border-white bg-[#2B2B2B] px-[10px] py-[8px] text-sm"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{rootStore.fileType?.startsWith("audio") ? (
|
{isMediaFile ? (
|
||||||
|
isAudioFile ? (
|
||||||
<AudioPlayer src={rootStore.fileSrc} />
|
<AudioPlayer src={rootStore.fileSrc} />
|
||||||
) : (
|
) : (
|
||||||
<ReactPlayer
|
<ReactPlayer
|
||||||
playsinline={true}
|
playsinline={true}
|
||||||
controls={true}
|
controls={true}
|
||||||
|
|
@ -149,6 +200,16 @@ export const DataStep = ({ nextStep }: DataStepProps) => {
|
||||||
config={{ file: { attributes: { playsInline: true } } }}
|
config={{ file: { attributes: { playsInline: true } } }}
|
||||||
url={rootStore.fileSrc}
|
url={rootStore.fileSrc}
|
||||||
/>
|
/>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<span className="font-semibold">{rootStore.file?.name}</span>
|
||||||
|
<span className="text-xs text-[#7B7B7B]">
|
||||||
|
{formatFileSize(rootStore.file?.size)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
onClick={handleFileReset}
|
onClick={handleFileReset}
|
||||||
|
|
@ -172,9 +233,15 @@ export const DataStep = ({ nextStep }: DataStepProps) => {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
onClick={() => rootStore.setAllowDwnld(!rootStore.allowDwnld)}
|
onClick={() => rootStore.setAllowDwnld(!rootStore.allowDwnld)}
|
||||||
checked={rootStore.allowDwnld}
|
checked={rootStore.allowDwnld}
|
||||||
|
disabled={rootStore.isDownloadLocked}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
{rootStore.isDownloadLocked && (
|
||||||
|
<p className="text-xs text-[#7B7B7B]">
|
||||||
|
Скачивание всегда доступно для документов и других не медиа-файлов.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
<FormLabel
|
<FormLabel
|
||||||
label={"Разрешить обложку"}
|
label={"Разрешить обложку"}
|
||||||
labelClassName={"flex"}
|
labelClassName={"flex"}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import { useCreateNewContent } from "~/shared/services/content";
|
||||||
import { BackButton } from "~/shared/ui/back-button";
|
import { BackButton } from "~/shared/ui/back-button";
|
||||||
import { useTonConnectUI } from "@tonconnect/ui-react";
|
import { useTonConnectUI } from "@tonconnect/ui-react";
|
||||||
import { ErrorUploadModal } from "./components/error-upload-modal";
|
import { ErrorUploadModal } from "./components/error-upload-modal";
|
||||||
|
import { AudioPlayer } from "~/shared/ui/audio-player";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -37,6 +38,9 @@ export const PresubmitStep = ({ prevStep }: PresubmitStepProps) => {
|
||||||
const createContent = useCreateNewContent();
|
const createContent = useCreateNewContent();
|
||||||
|
|
||||||
const [isErrorUploadModal, setIsErrorUploadModal] = useState(false);
|
const [isErrorUploadModal, setIsErrorUploadModal] = useState(false);
|
||||||
|
const isAudioFile = rootStore.fileType?.startsWith("audio");
|
||||||
|
const isVideoFile = rootStore.fileType?.startsWith("video");
|
||||||
|
const isMediaFile = isAudioFile || isVideoFile;
|
||||||
|
|
||||||
const handleErrorUploadModal = () => {
|
const handleErrorUploadModal = () => {
|
||||||
setIsErrorUploadModal(false);
|
setIsErrorUploadModal(false);
|
||||||
|
|
@ -285,21 +289,35 @@ export const PresubmitStep = ({ prevStep }: PresubmitStepProps) => {
|
||||||
|
|
||||||
<FormLabel label={"Файл"}>
|
<FormLabel label={"Файл"}>
|
||||||
{rootStore.fileSrc && !uploadFile.isUploading && (
|
{rootStore.fileSrc && !uploadFile.isUploading && (
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
"w-full border border-white bg-[#2B2B2B] px-[10px] py-[8px] text-sm"
|
"w-full border border-white bg-[#2B2B2B] px-[10px] py-[8px] text-sm"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ReactPlayer
|
{isMediaFile ? (
|
||||||
|
isAudioFile ? (
|
||||||
|
<AudioPlayer src={rootStore.fileSrc} />
|
||||||
|
) : (
|
||||||
|
<ReactPlayer
|
||||||
playsinline={true}
|
playsinline={true}
|
||||||
controls={true}
|
controls={true}
|
||||||
width="100%"
|
width="100%"
|
||||||
config={{ file: { attributes: { playsInline: true } } }}
|
config={{ file: { attributes: { playsInline: true } } }}
|
||||||
url={rootStore.fileSrc}
|
url={rootStore.fileSrc}
|
||||||
/>
|
/>
|
||||||
</div>
|
)
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<span className="font-semibold">{rootStore.file?.name}</span>
|
||||||
|
<span className="text-xs text-[#7B7B7B]">
|
||||||
|
{rootStore.file?.size
|
||||||
|
? `${(rootStore.file.size / (1024 * 1024)).toFixed(1)} МБ`
|
||||||
|
: ""}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{uploadFile.isUploading && uploadFile.isLoading && (
|
{uploadFile.isUploading && uploadFile.isLoading && (
|
||||||
<Progress value={uploadFile.uploadProgress} />
|
<Progress value={uploadFile.uploadProgress} />
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -36,37 +36,21 @@ export const ViewContentPage = () => {
|
||||||
const [isCongratsModal, setIsCongratsModal] = useState(false);
|
const [isCongratsModal, setIsCongratsModal] = useState(false);
|
||||||
const [isErrorModal, setIsErrorModal] = useState(false);
|
const [isErrorModal, setIsErrorModal] = useState(false);
|
||||||
|
|
||||||
const statusState = content?.data?.status?.state ?? "uploaded";
|
const displayOptions = content?.data?.display_options ?? {};
|
||||||
const conversionState = content?.data?.conversion?.state;
|
const mediaUrl = displayOptions?.content_url ?? null;
|
||||||
const uploadState = content?.data?.upload?.state;
|
const downloadUrl = displayOptions?.download_url ?? null;
|
||||||
const statusMessage = useMemo(() => {
|
const metadata = displayOptions?.metadata ?? {};
|
||||||
switch (statusState) {
|
const metadataTitle = metadata?.title || metadata?.name || content?.data?.encrypted?.title || 'Контент';
|
||||||
case "processing":
|
const metadataArtist = metadata?.artist || content?.data?.encrypted?.artist || null;
|
||||||
return "Контент обрабатывается";
|
const displayTitle = metadata?.display_name || (metadataArtist ? `${metadataArtist} – ${metadataTitle}` : metadataTitle);
|
||||||
case "failed":
|
const contentKind =
|
||||||
return "Ошибка обработки";
|
displayOptions?.content_kind ??
|
||||||
case "ready":
|
content?.data?.content_kind ??
|
||||||
return null;
|
((content?.data?.content_type ?? '').split('/')[0] || 'other');
|
||||||
default:
|
const isAudio = contentKind === 'audio';
|
||||||
return "Файл загружен";
|
const hasMediaPlayer = Boolean(mediaUrl);
|
||||||
}
|
const coverUrl = metadata?.image ?? null;
|
||||||
}, [statusState]);
|
const canDownload = Boolean(content?.data?.downloadable && downloadUrl);
|
||||||
|
|
||||||
const mediaUrl = content?.data?.display_options?.content_url ?? null;
|
|
||||||
const isAudio = Boolean(content?.data?.content_type?.startsWith('audio'));
|
|
||||||
const isReady = Boolean(mediaUrl);
|
|
||||||
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) {
|
||||||
console.error('No content identifier available for purchase');
|
console.error('No content identifier available for purchase');
|
||||||
|
|
@ -202,10 +186,10 @@ export const ViewContentPage = () => {
|
||||||
}, [haveLicense, refetchContent]);
|
}, [haveLicense, refetchContent]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (contentTitle) {
|
if (displayTitle) {
|
||||||
document.title = contentTitle;
|
document.title = displayTitle;
|
||||||
}
|
}
|
||||||
}, [contentTitle]);
|
}, [displayTitle]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!contentId) {
|
if (!contentId) {
|
||||||
|
|
@ -229,12 +213,17 @@ export const ViewContentPage = () => {
|
||||||
|
|
||||||
const handleDwnldContent = async () => {
|
const handleDwnldContent = async () => {
|
||||||
try {
|
try {
|
||||||
const fileUrl = content?.data?.display_options?.content_url;
|
const fileUrl = downloadUrl ?? mediaUrl;
|
||||||
const fileName = content?.data?.display_options?.metadata?.name || 'content';
|
if (!fileUrl) {
|
||||||
const fileFormat = content?.data?.content_ext || '.raw';
|
console.error('No file URL available for download');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fileName = metadataTitle || 'content';
|
||||||
|
const rawExtension = content?.data?.content_ext || metadata?.file_extension || 'raw';
|
||||||
|
const normalizedExtension = rawExtension.startsWith('.') ? rawExtension.slice(1) : rawExtension;
|
||||||
await WebApp.downloadFile({
|
await WebApp.downloadFile({
|
||||||
url: fileUrl,
|
url: fileUrl,
|
||||||
file_name: fileName + '.' + fileFormat,
|
file_name: `${fileName}.${normalizedExtension || 'raw'}`,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error downloading content:', error);
|
console.error('Error downloading content:', error);
|
||||||
|
|
@ -242,22 +231,22 @@ 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-[50px] px-4'}>
|
||||||
{isCongratsModal && <CongratsModal onConfirm={handleConfirmCongrats} />}
|
{isCongratsModal && <CongratsModal onConfirm={handleConfirmCongrats} />}
|
||||||
{isErrorModal && <ErrorModal onConfirm={handleErrorModal} />}
|
{isErrorModal && <ErrorModal onConfirm={handleErrorModal} />}
|
||||||
{isReady && isAudio &&
|
|
||||||
content?.data?.display_options?.metadata?.image && (
|
|
||||||
<div className={'mt-[30px] h-[314px] w-full'}>
|
|
||||||
<img
|
|
||||||
alt={'content_image'}
|
|
||||||
className={'h-full w-full object-cover object-center'}
|
|
||||||
src={content?.data?.display_options?.metadata?.image}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isReady ? (
|
{hasMediaPlayer ? (
|
||||||
<>
|
<>
|
||||||
|
{isAudio && coverUrl && (
|
||||||
|
<div className={'mt-[30px] h-[314px] w-full'}>
|
||||||
|
<img
|
||||||
|
alt={'content_image'}
|
||||||
|
className={'h-full w-full object-cover object-center'}
|
||||||
|
src={coverUrl}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{isAudio ? (
|
{isAudio ? (
|
||||||
<AudioPlayer src={mediaUrl ?? ''} />
|
<AudioPlayer src={mediaUrl ?? ''} />
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -270,7 +259,7 @@ export const ViewContentPage = () => {
|
||||||
attributes: {
|
attributes: {
|
||||||
playsInline: true,
|
playsInline: true,
|
||||||
autoPlay: true,
|
autoPlay: true,
|
||||||
poster: content?.data?.display_options?.metadata?.image || undefined,
|
poster: coverUrl || undefined,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
|
@ -279,82 +268,88 @@ export const ViewContentPage = () => {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<section className={'flex flex-col'}>
|
<section className={'flex flex-col'}>
|
||||||
<h1 className={'text-[20px] font-bold'}>{metadataName}</h1>
|
<h1 className={'text-[20px] font-bold'}>{displayTitle}</h1>
|
||||||
<p className={'mt-2 text-[12px]'}>
|
{metadata?.description && (
|
||||||
{content?.data?.display_options?.metadata?.description}
|
<p className={'mt-2 text-[12px]'}>
|
||||||
</p>
|
{metadata.description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</section>
|
</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 && (
|
|
||||||
<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 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">
|
||||||
|
{coverUrl && (
|
||||||
|
<div className="mx-auto mb-4 h-32 w-32 overflow-hidden rounded-xl">
|
||||||
|
<img
|
||||||
|
alt="content_cover"
|
||||||
|
className="h-full w-full object-cover object-center"
|
||||||
|
src={coverUrl}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<h1 className="text-lg font-semibold text-slate-100">
|
<h1 className="text-lg font-semibold text-slate-100">
|
||||||
Контент скоро будет здесь
|
Контент скоро будет доступен
|
||||||
</h1>
|
</h1>
|
||||||
<p className="mt-3 text-sm text-slate-300">
|
<p className="mt-2 text-sm font-semibold text-slate-100">
|
||||||
Мы уже обрабатываем загруженный файл и обновим страницу автоматически, как только появится доступ к полному контенту.
|
{displayTitle}
|
||||||
</p>
|
</p>
|
||||||
{statusMessage && (
|
{metadata?.description && (
|
||||||
<p className="mt-4 text-[12px] text-slate-500">
|
<p className="mt-3 text-sm text-slate-300">
|
||||||
Текущее состояние: {statusMessage}
|
{metadata.description}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{processingDetails?.conversion && (
|
{canDownload && (
|
||||||
<p className="mt-2 text-[12px] text-slate-500">
|
<p className="mt-4 text-xs text-slate-400">
|
||||||
Статус конвертера: {processingDetails.conversion}
|
Файл можно скачать ниже.
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
{processingDetails?.upload && (
|
|
||||||
<p className="mt-2 text-[12px] text-slate-500">
|
|
||||||
Загрузка: {processingDetails.upload}
|
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<div className="mt-auto pb-2">
|
||||||
|
{canDownload && (
|
||||||
|
<Button
|
||||||
|
onClick={handleDwnldContent}
|
||||||
|
className={'mb-4 h-[48px]'}
|
||||||
|
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={'mt-4 h-[48px] bg-darkred'}
|
||||||
|
label={`Отключить кошелек`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ const VIEW_CONTENT_API_PATH = "/api/v1";
|
||||||
|
|
||||||
type UseCreateNewContentPayload = {
|
type UseCreateNewContentPayload = {
|
||||||
title: string;
|
title: string;
|
||||||
|
artist?: string;
|
||||||
content: string;
|
content: string;
|
||||||
image: string;
|
image: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
|
@ -39,6 +40,7 @@ export const useCreateNewContent = () => {
|
||||||
royaltyCount: payload.royaltyParams.length,
|
royaltyCount: payload.royaltyParams.length,
|
||||||
downloadable: payload.downloadable,
|
downloadable: payload.downloadable,
|
||||||
allowResale: payload.allowResale,
|
allowResale: payload.allowResale,
|
||||||
|
artist: payload.artist,
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -46,7 +48,7 @@ export const useCreateNewContent = () => {
|
||||||
address: string;
|
address: string;
|
||||||
amount: string;
|
amount: string;
|
||||||
payload: string;
|
payload: string;
|
||||||
}>("/blockchain.sendNewContentMessage", payload, {
|
}>("/blockchain.sendNewContentMessage", payload, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: localStorage.getItem('auth_v1_token') ?? ""
|
Authorization: localStorage.getItem('auth_v1_token') ?? ""
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,8 @@ type RootStore = {
|
||||||
|
|
||||||
allowDwnld: boolean;
|
allowDwnld: boolean;
|
||||||
setAllowDwnld: (allowDwnld: boolean) => void;
|
setAllowDwnld: (allowDwnld: boolean) => void;
|
||||||
|
isDownloadLocked: boolean;
|
||||||
|
setDownloadLocked: (locked: boolean) => void;
|
||||||
|
|
||||||
cover: File | null;
|
cover: File | null;
|
||||||
setCover: (cover: File | null) => void;
|
setCover: (cover: File | null) => void;
|
||||||
|
|
@ -86,6 +88,8 @@ export const useRootStore = create<RootStore>((set) => ({
|
||||||
|
|
||||||
allowDwnld: false,
|
allowDwnld: false,
|
||||||
setAllowDwnld: (allowDwnld) => set({ allowDwnld }),
|
setAllowDwnld: (allowDwnld) => set({ allowDwnld }),
|
||||||
|
isDownloadLocked: false,
|
||||||
|
setDownloadLocked: (isDownloadLocked) => set({ isDownloadLocked }),
|
||||||
|
|
||||||
cover: null,
|
cover: null,
|
||||||
setCover: (cover) => set({ cover }),
|
setCover: (cover) => set({ cover }),
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,16 @@ import { useHapticFeedback } from "@vkruglikov/react-telegram-web-app";
|
||||||
type CheckboxProps = {
|
type CheckboxProps = {
|
||||||
onClick(): void;
|
onClick(): void;
|
||||||
checked: boolean;
|
checked: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Checkbox = ({ onClick, checked }: CheckboxProps) => {
|
export const Checkbox = ({ onClick, checked, disabled }: CheckboxProps) => {
|
||||||
const [impactOccurred] = useHapticFeedback();
|
const [impactOccurred] = useHapticFeedback();
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
|
if (disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
impactOccurred("light");
|
impactOccurred("light");
|
||||||
onClick();
|
onClick();
|
||||||
};
|
};
|
||||||
|
|
@ -16,7 +20,7 @@ export const Checkbox = ({ onClick, checked }: CheckboxProps) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
className={"flex h-8 w-8 items-center justify-center bg-[#2B2B2B] p-2"}
|
className={"flex h-8 w-8 items-center justify-center bg-[#2B2B2B] p-2" + (disabled ? " opacity-40" : "")}
|
||||||
>
|
>
|
||||||
{checked && <div className={"h-full w-full bg-primary"} />}
|
{checked && <div className={"h-full w-full bg-primary"} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue