diff --git a/public/locales/vn/common.json b/public/locales/vn/common.json index 3989653a..1ab18739 100644 --- a/public/locales/vn/common.json +++ b/public/locales/vn/common.json @@ -15,6 +15,8 @@ "Unknown creator": "Tác giả ẩn danh", "views": "luợt xem", "likes": "lượt thích", + "view": "luợt xem", + "like": "lượt thích", "Latest": "Mới nhất", "Chap": "Chương", "Subscribed": "Đã đăng ký", @@ -33,6 +35,7 @@ "Please": "Vui lòng", "to countinue reading": "để tiếp tục đọc", "comments": "bình luận", + "comment": "bình luận", "Previous chap": "Chương trước", "Next chap": "Chương sau", "See Detail": "Chi tiết", @@ -45,7 +48,7 @@ "Only digits": "Chỉ nhập chữ số", "All types": "Tất cả loại", "Free": "Miễn phí", - "NFTs only": "Cần NFT", + "NFTs only": "Yêu cầu NFT", "Sort by newest": "Mới nhất", "Sort by oldest": "Cũ nhất", "Search by title": "Tìm kiếm truyện và tác giả", diff --git a/src/components/pages/chapter/chapterList.tsx b/src/components/pages/chapter/chapterList.tsx index 11f11416..066fcbbd 100644 --- a/src/components/pages/chapter/chapterList.tsx +++ b/src/components/pages/chapter/chapterList.tsx @@ -17,6 +17,7 @@ import Image from 'next/image' import { useRouter } from 'next/router' import { useContext, useEffect, useState } from 'react' import m6 from 'src/assets/images/mockup6.png' +import { CHAPTER_STATUS, CHAPTER_TYPE } from 'src/constants/chapter.constant' import { Context } from 'src/context' import { getBlurUrl } from 'src/utils' @@ -86,15 +87,15 @@ export default function ChapterList({ }, { - key: 'Free', + key: CHAPTER_TYPE.FREE, value: t('Free'), }, { - key: 'NFTs only', + key: CHAPTER_TYPE.NFTS_ONLY, value: t('NFTs only'), }, { - key: 'Account only', + key: CHAPTER_TYPE.ACCOUNT_ONLY, value: t('Account only'), }, ]} @@ -206,6 +207,10 @@ const Chapter = ({ ;(document.querySelector('#open-sign-in-btn') as any)?.click() } } + const unavailable = + chapter.status == CHAPTER_STATUS.UPCOMING || + (!account && chapter.type == CHAPTER_TYPE.ACCOUNT_ONLY) || + (!data.hasAccess && chapter.type == CHAPTER_TYPE.NFTS_ONLY) return ( <>
{ - if (chapter.status == 'Upcoming' || (!account && chapter.type == 'Account only')) return + if (unavailable) return setExpandDetail(false) router.push(`/comic/${data.id}/chapter/${chapter.number}`) }} @@ -235,7 +240,7 @@ const Chapter = ({

{`${t('Chapter')} ${chapter.number}`}

{(function () { switch (chapter.type) { - case 'Account only': + case CHAPTER_TYPE.ACCOUNT_ONLY: if (account) { return ( @@ -252,19 +257,30 @@ const Chapter = ({ ) } - case 'Upcoming': - return ( - - <>{t('Upcoming')} - - ) + case CHAPTER_TYPE.NFTS_ONLY: + if (data.hasAccess) { + return ( + + <>{t('NFTs only')} + + ) + } else { + return ( + +
+ + {t('NFTs only')} +
+
+ ) + } default: return
} })()} {(function () { switch (chapter.status) { - case 'Upcoming': + case CHAPTER_STATUS.UPCOMING: return ( <>{t('Upcoming')} @@ -278,7 +294,7 @@ const Chapter = ({
{ - if (chapter.status == 'Upcoming' || (!account && chapter.type == 'Account only')) return + if (unavailable) return setExpandDetail(false) router.push(`/comic/${data.id}/chapter/${chapter.number}`) }}> diff --git a/src/components/pages/chapter/comicDetail.tsx b/src/components/pages/chapter/comicDetail.tsx index 141f0f54..8aed1188 100644 --- a/src/components/pages/chapter/comicDetail.tsx +++ b/src/components/pages/chapter/comicDetail.tsx @@ -19,6 +19,7 @@ import ChapterList from './chapterList' import { Tab } from '@headlessui/react' import Ninja from 'images/ninja-2.svg' import Link from 'next/link' +import { CHAPTER_STATUS } from 'src/constants/chapter.constant' export default function ComicDetail({ data, @@ -157,7 +158,7 @@ export default function ComicDetail({ ))}

- {data.views?.toLocaleString('en-US')} {t('views')} + {data.views?.toLocaleString('en-US')} {data.views > 1 ? t('views') : t('view')} - {comicLikes?.toLocaleString('en-US')} {t('likes')} + {comicLikes?.toLocaleString('en-US')} {comicLikes > 1 ? t('likes') : t('like')}

@@ -218,7 +219,7 @@ export default function ComicDetail({

)}
- {data.chapters.find((chapter) => chapter.status === 'Upcoming')?.date && ( + {data.chapters.find((chapter) => chapter.status === CHAPTER_STATUS.UPCOMING)?.date && (
{' '}
{`${t('New chapter arrives on')} - ${moment(data.chapters.find((chapter) => chapter.status === 'Upcoming')?.date).format( + ${moment(data.chapters.find((chapter) => chapter.status === CHAPTER_STATUS.UPCOMING)?.date).format( 'dddd, DD/MM/yyyy' )}. ${t('Don’t miss latest update, subscribe now')}!`} diff --git a/src/components/pages/chapter/headerBar.tsx b/src/components/pages/chapter/headerBar.tsx index 9ed15d49..8d48b774 100644 --- a/src/components/pages/chapter/headerBar.tsx +++ b/src/components/pages/chapter/headerBar.tsx @@ -12,6 +12,7 @@ import Image from 'next/image' import { useRouter } from 'next/router' import { useContext, useState } from 'react' import { useTranslation } from 'react-i18next' +import { CHAPTER_STATUS } from 'src/constants/chapter.constant' import { Context } from 'src/context' export default function HeaderBar({ openComments, @@ -88,7 +89,8 @@ export default function HeaderBar({
- {!account && chapterData.type == 'Account only' ? ( + {!account && chapterData.type == CHAPTER_TYPE.ACCOUNT_ONLY ? (

@@ -260,9 +261,9 @@ export default function ReadingSection({

{`Chapter ${chapterData.number} • ${chapterData.name}`}

- {(chapterLikes || 0).toLocaleString('en-US')} {t('likes')} •{' '} - {(chapterData.views || 0).toLocaleString('en-US')} {t('views')} •{' '} - {(commentNumber || 0).toLocaleString('en-US')} {t('comments')} + {(chapterLikes || 0).toLocaleString('en-US')} {chapterLikes > 1 ? t('likes') : t('like')} •{' '} + {(chapterData.views || 0).toLocaleString('en-US')} {chapterData.views > 1 ? t('views') : t('view')} •{' '} + {(commentNumber || 0).toLocaleString('en-US')} {commentNumber > 1 ? t('comments') : t('comment')}

@@ -275,7 +276,9 @@ export default function ReadingSection({ goToChap('Next')} - disabled={currentChapIndex == 0 || data.chapters?.[currentChapIndex - 1]?.status == 'Upcoming'}> + disabled={ + currentChapIndex == 0 || data.chapters?.[currentChapIndex - 1]?.status == CHAPTER_STATUS.UPCOMING + }>
{t('Next chap')} @@ -332,9 +335,9 @@ export default function ReadingSection({
{`${t('Chapter')} ${chapterData.number} • ${chapterData.name}`}

- {(chapterLikes || 0).toLocaleString('en-US')} {t('likes')} •{' '} - {(chapterData.views || 0).toLocaleString('en-US')} {t('views')} •{' '} - {(commentNumber || 0).toLocaleString('en-US')} {t('comments')} + {(chapterLikes || 0).toLocaleString('en-US')} {chapterLikes > 1 ? t('likes') : t('like')} •{' '} + {(chapterData.views || 0).toLocaleString('en-US')} {chapterData.views > 1 ? t('views') : t('view')} •{' '} + {(commentNumber || 0).toLocaleString('en-US')} {commentNumber > 1 ? t('comments') : t('comment')}

@@ -386,7 +389,7 @@ export default function ReadingSection({
(isDesc ? 1 : -1)) .map((chapter, index) => ( - + ))}
@@ -69,11 +77,13 @@ const Chapter = ({ like, unlike, setComicLikes, + hasAccess, }: { chapter: IChapter like: any unlike: any setComicLikes: any + hasAccess: boolean }) => { const router = useRouter() const { query } = useRouter() @@ -97,15 +107,13 @@ const Chapter = ({ ;(document.querySelector('#open-sign-in-btn') as any)?.click() } } + const unavailable = + chapter.status == CHAPTER_STATUS.UPCOMING || + (!account && chapter.type == CHAPTER_TYPE.ACCOUNT_ONLY) || + (!hasAccess && chapter.type == CHAPTER_TYPE.NFTS_ONLY) return (
- chapter.status != 'Upcoming' - ? !(!account && chapter.type == 'Account only') - ? router.push(`/comic/${query.comicId}/chapter/${chapter.number}`) - : null - : null - } + onClick={() => (!unavailable ? router.push(`/comic/${query.comicId}/chapter/${chapter.number}`) : null)} className='flex border-bottom border-[#414141] bg-[#292929]/80 text-white relative'> {(function () { switch (chapter.type) { - case 'Account only': + case CHAPTER_TYPE.ACCOUNT_ONLY: if (account) { return ( @@ -134,6 +142,23 @@ const Chapter = ({ ) } + case CHAPTER_TYPE.NFTS_ONLY: + if (hasAccess) { + return ( + + <>{t('NFTs only')} + + ) + } else { + return ( + +
+ + {t('NFTs only')} +
+
+ ) + } default: return
} @@ -143,24 +168,6 @@ const Chapter = ({

{`${t('Chapter')} ${chapter.number}`}

- {/* {(function () { - switch (chapter.status) { - case 'Published': - return <> - case 'Upcoming': - return ( - - <>{t('Upcoming')} - - ) - default: - return ( -
- <>{t(chapter.status)} -
- ) - } - })()} */}
{chapter.name}
diff --git a/src/components/pages/homepage/comic.tsx b/src/components/pages/homepage/comic.tsx index 04c86c3b..24f5bf76 100644 --- a/src/components/pages/homepage/comic.tsx +++ b/src/components/pages/homepage/comic.tsx @@ -75,10 +75,10 @@ export default function Comic(props: IComic) {
- {props.views.toLocaleString('en-US')} {t('views')} + {props.views.toLocaleString('en-US')} {props.views > 1 ? t('views') : t('view')}
- {props.likes.toLocaleString('en-US')} {t('likes')} + {props.likes.toLocaleString('en-US')} {props.likes > 1 ? t('likes') : t('like')}
{props[locale].description}
diff --git a/src/components/pages/profile/comic.tsx b/src/components/pages/profile/comic.tsx index 04087e50..ddb76aa7 100644 --- a/src/components/pages/profile/comic.tsx +++ b/src/components/pages/profile/comic.tsx @@ -58,10 +58,10 @@ export default function Comic(props: IComic & { unsubscribe?: () => void; subscr
- {props.views.toLocaleString('en-US')} {t('views')} + {props.views.toLocaleString('en-US')} {props.views > 1 ? t('views') : t('view')}
- {props.likes.toLocaleString('en-US')} {t('likes')} + {props.likes.toLocaleString('en-US')} {props.likes > 1 ? t('likes') : t('like')}
{props[locale].description}
diff --git a/src/constants/chapter.constant.ts b/src/constants/chapter.constant.ts new file mode 100644 index 00000000..f5d58a73 --- /dev/null +++ b/src/constants/chapter.constant.ts @@ -0,0 +1,10 @@ +export enum CHAPTER_TYPE { + NFTS_ONLY = 'NFTs only', + ACCOUNT_ONLY = 'Account only', + FREE = 'Free', +} +export enum CHAPTER_STATUS { + PUBLISHED = 'Published', + UPCOMING = 'Upcoming', + INACTIVE = 'Inactive', +} diff --git a/src/models/comic.ts b/src/models/comic.ts index da77c84e..723f9a41 100644 --- a/src/models/comic.ts +++ b/src/models/comic.ts @@ -32,6 +32,7 @@ export interface IComic interface Detail { cover: string + hasAccess: boolean languages: Array< (typeof LANGUAGE)[number] & { isMainLanguage: boolean diff --git a/src/pages/comic/[comicId]/chapter/[chapterNumber]/chapter.tsx b/src/pages/comic/[comicId]/chapter/[chapterNumber]/chapter.tsx index a95817e3..f1c40156 100644 --- a/src/pages/comic/[comicId]/chapter/[chapterNumber]/chapter.tsx +++ b/src/pages/comic/[comicId]/chapter/[chapterNumber]/chapter.tsx @@ -20,6 +20,7 @@ import { useRouter } from 'next/router' import React, { useContext, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { useScrollDirection } from 'react-use-scroll-direction' +import { CHAPTER_TYPE } from 'src/constants/chapter.constant' import { LanguageType } from 'src/constants/global.types' import { Context } from 'src/context' import { IChapter } from 'src/models/chapter' @@ -181,7 +182,7 @@ const Chapter: React.FC = ({ postComment={postComment} />
- {!account && chapterDetails.data.type == 'Account only' ? ( + {!account && chapterDetails.data.type == CHAPTER_TYPE.ACCOUNT_ONLY ? (

diff --git a/src/pages/comic/[comicId]/chapter/[chapterNumber]/with-api.tsx b/src/pages/comic/[comicId]/chapter/[chapterNumber]/with-api.tsx index d7befa59..809de101 100644 --- a/src/pages/comic/[comicId]/chapter/[chapterNumber]/with-api.tsx +++ b/src/pages/comic/[comicId]/chapter/[chapterNumber]/with-api.tsx @@ -2,79 +2,18 @@ import axios from 'axios' import getConfig from 'next/config' import { useRouter } from 'next/router' import { useContext, useRef } from 'react' -import { COMIC_STATUS, LANGUAGE } from 'src/constants' +import { LANGUAGE } from 'src/constants' import { Context, privateAxios } from 'src/context' import useApi from 'src/hooks/useApi' import { IChapter } from 'src/models/chapter' import { IComicDetail } from 'src/models/comic' import { IComment } from 'src/models/comment' -import { formatStatus } from 'src/utils' +import { getComicDetail } from 'src/services' const withApi = (Component: React.FC) => (props: any) => { const { query } = useRouter() const { account } = useContext(Context) const config = getConfig() const chapterId = useRef() - const getComicDetail = async () => { - const d: any = await axios.get(`${config.API_URL}/api/rest/public/manga/${query.comicId}`, { - params: { - user_id: account?.id, - }, - }) - const data = d?.data?.manga[0] - - const res = { - id: data.id, - languages: data.manga_languages.map((ml) => ({ - ...LANGUAGE.find((l) => l.id == ml.language_id), - isMainLanguage: ml.is_main_language, - })), - chapters: data.chapters.map((chapter) => ({ - id: chapter.id, - name: chapter.chapter_name, - number: chapter.chapter_number, - type: chapter.chapter_type, - status: formatStatus(chapter.status), - thumbnail: chapter.thumbnail_url, - date: chapter.pushlish_date, - likes: chapter.chapter_total_likes?.likes || 0, - views: chapter.views || 0, - isLiked: !!chapter.chapters_likes?.length, - })), - status: { - type: COMIC_STATUS[formatStatus(data.status)], - text: formatStatus(data.status), - }, - views: data.manga_total_views?.views || 0, - likes: data.manga_total_likes?.likes || 0, - isSubscribe: !!data.manga_subscribers.length, - image: data.poster, - cover: data.banner, - tags: data.manga_tags.map(({ tag }: any) => { - const r = {} - LANGUAGE.forEach((l) => { - const tagLanguage = tag.tag_languages.find((tl) => tl.language_id == l.id) || tag.tag_languages[0] - r[l.shortLang] = tagLanguage.value - }) - return r - }), - authors: data.manga_creators?.map((c: any) => ({ - id: c.creator?.isActive ? c.creator?.id : undefined, - name: c.creator?.isActive ? c.creator?.name : 'Unknown creator', - })), - releaseDate: data.release_date, - } - - LANGUAGE.forEach((l) => { - const mangaLanguages = - data.manga_languages.find((ml) => ml.language_id == l.id) || - data.manga_languages.find((ml) => ml.is_main_language) - res[l.shortLang] = { - title: mangaLanguages?.title, - description: mangaLanguages?.description, - } - }) - return res - } const getChapterDetails = async () => { const { @@ -144,7 +83,11 @@ const withApi = (Component: React.FC) => (props: any) => { return [] } - const comicDetails = useApi(getComicDetail, !!query.comicId, [query.comicId, account?.id]) + const comicDetails = useApi( + async () => await getComicDetail(query.comicId as string, account?.id), + !!query.comicId, + [query.comicId, account?.id] + ) const chapterComments = useApi(getChapterComments, !!chapterId.current, [chapterId.current]) const chapterDetails = useApi(getChapterDetails, !!query.comicId && !!query.chapterNumber, [ query.comicId, diff --git a/src/pages/comic/[comicId]/comic.tsx b/src/pages/comic/[comicId]/comic.tsx index 47c90c9a..0039e121 100644 --- a/src/pages/comic/[comicId]/comic.tsx +++ b/src/pages/comic/[comicId]/comic.tsx @@ -156,7 +156,13 @@ export default function Comic({ comicDetails, subscribe, unsubscribe, like, unli - +

diff --git a/src/pages/comic/[comicId]/with-api.tsx b/src/pages/comic/[comicId]/with-api.tsx index f390906b..467ee991 100644 --- a/src/pages/comic/[comicId]/with-api.tsx +++ b/src/pages/comic/[comicId]/with-api.tsx @@ -1,71 +1,14 @@ -import axios from 'axios' import getConfig from 'next/config' import { useRouter } from 'next/router' import { useContext } from 'react' -import { LANGUAGE } from 'src/constants' import { Context, privateAxios } from 'src/context' import useApi from 'src/hooks/useApi' import { IComicDetail } from 'src/models/comic' -import { formatStatus } from 'src/utils' +import { getComicDetail } from 'src/services' const withApi = (Component: React.FC) => (props: any) => { const { account } = useContext(Context) const { query } = useRouter() const config = getConfig() - const getComicDetail = async () => { - const d: any = await axios.get(`${config.API_URL}/api/rest/public/manga/${query.comicId}`, { - params: { - user_id: account?.id, - }, - }) - const data = d?.data?.manga[0] - const res = { - id: data.id, - languages: data.manga_languages.map((ml) => ({ - ...LANGUAGE.find((l) => l.id == ml.language_id), - isMainLanguage: ml.is_main_language, - })), - chapters: data.chapters.map((chapter) => ({ - id: chapter.id, - name: chapter.chapter_name, - number: chapter.chapter_number, - type: chapter.chapter_type, - status: formatStatus(chapter.status), - thumbnail: chapter.thumbnail_url, - date: chapter.pushlish_date, - likes: chapter.chapter_total_likes?.likes || 0, - views: chapter.views || 0, - isLiked: !!chapter.chapters_likes?.length, - })), - views: data.manga_total_views?.views || 0, - likes: data.manga_total_likes?.likes || 0, - isSubscribe: !!data.manga_subscribers.length, - image: data.poster, - cover: data.banner, - tags: data.manga_tags.map(({ tag }: any) => { - const r = {} - LANGUAGE.forEach((l) => { - const tagLanguage = tag.tag_languages.find((tl) => tl.language_id == l.id) || tag.tag_languages[0] - r[l.shortLang] = tagLanguage.value - }) - return r - }), - authors: data.manga_creators?.map((c: any) => ({ - id: c.creator?.isActive ? c.creator?.id : undefined, - name: c.creator?.isActive ? c.creator?.name : 'Unknown creator', - })), - } - - LANGUAGE.forEach((l) => { - const mangaLanguages = - data.manga_languages.find((ml) => ml.language_id == l.id) || - data.manga_languages.find((ml) => ml.is_main_language) - res[l.shortLang] = { - title: mangaLanguages?.title, - description: mangaLanguages?.description, - } - }) - return res - } const subscribe = async () => { await privateAxios.post(`${config.API_URL}/api/rest/user/manga/${query.comicId}/subscribe`) } @@ -80,7 +23,11 @@ const withApi = (Component: React.FC) => (props: any) => { await privateAxios.delete(`${config.API_URL}/api/rest/user/chapters/${id}/likes`) } - const comicDetails = useApi(getComicDetail, !!query.comicId, [query.comicId, account?.id]) + const comicDetails = useApi( + async () => await getComicDetail(query.comicId as string, account?.id), + !!query.comicId, + [query.comicId, account?.id] + ) return ( { return response }) } +export const getComicDetail = async (comicId: string, accountId: string) => { + const d: any = await axios.get(`${getConfig().API_URL}/api/rest/public/manga/${comicId}`, { + params: { + user_id: accountId, + }, + }) + const data = d?.data?.manga[0] + const hasAccess = await getAccess(data.id) + const res = { + id: data.id, + languages: data.manga_languages.map((ml) => ({ + ...LANGUAGE.find((l) => l.id == ml.language_id), + isMainLanguage: ml.is_main_language, + })), + hasAccess: hasAccess, + chapters: data.chapters.map((chapter) => ({ + id: chapter.id, + name: chapter.chapter_name, + number: chapter.chapter_number, + type: chapter.chapter_type, + status: formatStatus(chapter.status), + thumbnail: chapter.thumbnail_url, + date: chapter.pushlish_date, + likes: chapter.chapter_total_likes?.likes || 0, + views: chapter.views || 0, + isLiked: !!chapter.chapters_likes?.length, + })), + status: { + type: COMIC_STATUS[formatStatus(data.status)], + text: formatStatus(data.status), + }, + views: data.manga_total_views?.views || 0, + likes: data.manga_total_likes?.likes || 0, + isSubscribe: !!data.manga_subscribers.length, + image: data.poster, + cover: data.banner, + tags: data.manga_tags.map(({ tag }: any) => { + const r = {} + LANGUAGE.forEach((l) => { + const tagLanguage = tag.tag_languages.find((tl) => tl.language_id == l.id) || tag.tag_languages[0] + r[l.shortLang] = tagLanguage.value + }) + return r + }), + authors: data.manga_creators?.map((c: any) => ({ + id: c.creator?.isActive ? c.creator?.id : undefined, + name: c.creator?.isActive ? c.creator?.name : 'Unknown creator', + })), + releaseDate: data.release_date, + } + + LANGUAGE.forEach((l) => { + const mangaLanguages = + data.manga_languages.find((ml) => ml.language_id == l.id) || + data.manga_languages.find((ml) => ml.is_main_language) + res[l.shortLang] = { + title: mangaLanguages?.title, + description: mangaLanguages?.description, + } + }) + return res +} +export const getAccess = async (id: number) => { + try { + const { data } = await privateAxios.get(`${getConfig().REST_API_URL}/manga/${id}/get-access`) + return data?.nft + } catch (error) { + console.log(error) + return false + } +}