feat(web): reconstruct

Signed-off-by: Hanif Dwy Putra S <hanifdwyputrasembiring@gmail.com>
This commit is contained in:
Hanif Dwy Putra S
2022-08-12 12:45:33 +00:00
parent 8617ccf332
commit 67debcd265
3 changed files with 106 additions and 89 deletions

View File

@@ -23,6 +23,13 @@ export type ExtractedInfoWithProvider = ExtractedInfo & {
_url: string;
};
interface StateData {
submitted: boolean;
error?: string | Error;
url: string;
wasSubmit: boolean;
}
const fetcher: Fetcher<ExtractedInfoWithProvider, string> = (...args) =>
fetch(...args).then((r) => r.json());
@@ -31,18 +38,22 @@ const fetcher: Fetcher<ExtractedInfoWithProvider, string> = (...args) =>
* @return {JSX.Element}
*/
export const FormInputComponent = (): JSX.Element => {
const [url, setUrl] = React.useState('');
const [error, setError] = React.useState<string | Error>();
const [submitted, setSubmit] = React.useState<boolean>(false);
const [state, setState] = React.useState<StateData>({
submitted: false,
error: undefined,
url: '',
wasSubmit: false,
});
const {data, mutate} = useSWR(
submitted &&
(!error || !(error as string).length) &&
/^http(s?)(:\/\/)([a-z]+\.)*tiktok\.com\/(.+)$/gi.test(url)
(state.submitted || state.wasSubmit) &&
(!state.error || !(state.error as string).length) &&
/^http(s?)(:\/\/)([a-z]+\.)*tiktok\.com\/(.+)$/gi.test(state.url)
? [
'/api/download',
{
method: 'POST',
body: JSON.stringify({url}),
body: JSON.stringify({url: state.url}),
},
]
: null,
@@ -50,39 +61,59 @@ export const FormInputComponent = (): JSX.Element => {
{
loadingTimeout: 10_000,
refreshInterval: 30_000,
revalidateIfStale: true,
revalidateOnMount: false,
onSuccess: () =>
setState({
...state,
submitted: false,
}),
},
);
React.useEffect(() => {
if (
!/^http(s?)(:\/\/)([a-z]+\.)*tiktok\.com\/(.+)$/gi.test(url) &&
url.length
!/^http(s?)(:\/\/)([a-z]+\.)*tiktok\.com\/(.+)$/gi.test(
state.url,
) &&
state.url.length
) {
setError(new InvalidUrlError('Invalid TikTok Video URL'));
setState({
...state,
error: new InvalidUrlError('Invalid TikTok URL'),
});
} else {
// submit event trigger.
if (submitted && !error) {
if (state.submitted && !state.error) {
mutate();
}
try {
const u = getTikTokURL(url);
const u = getTikTokURL(state.url);
if (!u) {
setError(new InvalidUrlError('Invalid TikTok URL'));
setState({
...state,
error: new InvalidUrlError('Invalid TikTok URL'),
});
return;
}
console.log(u);
setUrl(u);
setState({
...state,
url: u,
});
} catch {
setError(new InvalidUrlError('Invalid TikTok Video URL'));
setState({
...state,
error: new InvalidUrlError('Invalid TikTok URL'),
});
}
setError(undefined);
setState({
...state,
error: undefined,
});
}
}, [url, submitted]);
}, [state.submitted, state.url]);
return (
<React.Fragment>
@@ -91,28 +122,42 @@ export const FormInputComponent = (): JSX.Element => {
Fill TikTok's Video URL below:
</h1>
<p className="text-red-400 font-sans font-semibold">
{error instanceof Error
? error.name.concat(': '.concat(error.message))
: error
? error
{state.error instanceof Error
? state.error.name.concat(
': '.concat(state.error.message),
)
: state.error
? state.error
: ''}
</p>
<form
className="flex flex-col md:flex-row"
onSubmit={(event) => {
event.preventDefault();
if (!url.length) {
setError('Please fill the URL!');
if (!state.url.length) {
setState({
...state,
error: 'Please fill the URL!',
});
return;
}
!error && setSubmit(true);
!state.error &&
setState({
...state,
submitted: true,
});
}}
>
<div>
<input
type="url"
onChange={(event) => setUrl(event.target.value)}
value={url}
onChange={(event) =>
setState({
...state,
url: event.target.value,
})
}
value={state.url}
placeholder="e.g: "
className="p-3 border border-gray-300 font-sans h-auto w-auto outline-solid-blue-500"
/>
@@ -121,7 +166,7 @@ export const FormInputComponent = (): JSX.Element => {
<div>
<button
className="p-3 lg:ml-2 mt-1 bg-sky-400 uppercase text-white shadow-sm"
disabled={submitted}
disabled={state.submitted}
>
download
</button>
@@ -129,14 +174,13 @@ export const FormInputComponent = (): JSX.Element => {
</form>
<section className="mt-3 mb-3">
{submitted && !data ? (
{state.submitted && !data && (
<p className={'text-base font-sans text-blue-500'}>
Wait a minute
</p>
) : (
data &&
data.video &&
data.video.urls.length && <VideoComponent data={data} />
)}
{data && data && data.video && data.video.urls.length && (
<VideoComponent data={data} />
)}
</section>
</section>

View File

@@ -2,64 +2,37 @@ import React from 'react';
import type {ExtractedInfoWithProvider} from './FormInput';
export const VideoComponent = ({data}: {data: ExtractedInfoWithProvider}) => {
const copyUrl = (url: string) => {
navigator.clipboard.writeText(url);
if (typeof window !== 'undefined') {
window.alert('URL Copied');
}
};
return (
<React.Fragment>
<h1 className="text-2xl text-center">Your Video Is Ready!</h1>
<div className="grid grid-cols-3 gap-3">
<div>
<video
autoPlay={false}
controls
className="h-64 w-80 rounded-md md:ml-2"
>
<source src={data.video?.urls[0]} />
</video>
This video is downloaded from{' '}
<span className="font-semibold">{data.provider}</span>.
{data.caption && <pre>{data.caption}</pre>}
<div className="md:grid md:grid-cols-3 md:gap-4">
<video
controls={true}
autoPlay={false}
className="rounded-md h-64 w-80"
>
<source src={data.video?.urls[0]} />
</video>
<div className="flex flex-row font-sans basis-8 mt-2">
{data.video?.urls.map((url, index) => (
<button
key={index.toString()}
className="mr-1 bg-teal-400 md:p-2 p-1 rounded-md shadow"
onClick={() => copyUrl(url)}
>
LINK {index + 1}
</button>
))}
</div>
<div className="bg-teal-200 rounded-sm text-center">
<ul className="grid grid-cols-1 gap-1">
<li key="download-url">
<p className="font-semibold">Download URLs:</p>
</li>
{data.video!.urls.map((url, index) => (
<li key={index}>
<a href={url}>Click to Download #{index + 1}</a>
</li>
))}
</ul>
</div>
{data.music && (
<div className="bg-light-500 rounded-sm text-center">
<ul className="grid grid-cols-1 gap-1">
<li key="music-head">
<p className="font-semibold">Music:</p>
</li>
<li key="music-url">
Music URL:{' '}
<a
href={data.music.url}
className="text-blue-500 uppercase"
target="_blank"
>
Click here
</a>
</li>
{data.music.author && (
<li key="music-author">
Music Author: {data.music.author}
</li>
)}
{data.music.title && (
<li key="music-title">
Music Title: {data.music.title}
</li>
)}
</ul>
</div>
)}
</div>
<p className="font-sans text-base mt-2">
&copy; Source: {data.provider}
</p>
</React.Fragment>
);
};

View File

@@ -11,7 +11,7 @@ const FormInputComponentDynamic = dynamic(
export default () => {
return (
<section className="p-5">
<h1 className="align-middle text-4xl font-sans font-medium tracking-wide leading-relaxed">
<h1 className="align-middle text-4xl font-sans font-medium">
TikTok-DL{' '}
<span className="font-normal md:break-words text-2xl">
Download TikTok Video without watermark and free ads.