added chapter view, fixes, added ratings, added chapter updates/deletions
This commit is contained in:
parent
029b7e9353
commit
4801cf3a13
34 changed files with 1345 additions and 191 deletions
104
app/client/pages/work/book/$bookId/chapter/$chapterId/edit.tsx
Normal file
104
app/client/pages/work/book/$bookId/chapter/$chapterId/edit.tsx
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
import { gql, useQuery } from '@apollo/client';
|
||||
import { BookChapterForm } from '@app/client/components/BookChapterForm';
|
||||
import { useIdentity } from '@app/client/hooks/identity';
|
||||
import { useToastMutation } from '@app/client/hooks/useToastMutation';
|
||||
import { Skeleton } from '@chakra-ui/react';
|
||||
import { BookChapterEditQuery, BookChapterNewCreateMutation } from '@generated/types';
|
||||
import { createFileRoute, redirect, useNavigate } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/work/book/$bookId/chapter/$chapterId/edit')({
|
||||
component: RouteComponent,
|
||||
beforeLoad: ({ context }) => {
|
||||
if (!context.isAuthenticated) throw redirect({ to: '/' });
|
||||
}
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const { bookId, chapterId } = Route.useParams();
|
||||
|
||||
const identity = useIdentity();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { data } = useQuery<BookChapterEditQuery>(query, {
|
||||
variables: { id: chapterId }
|
||||
});
|
||||
|
||||
const { update: chapterUpdate } = useToastMutation<BookChapterNewCreateMutation>({
|
||||
mutation,
|
||||
successMsg: 'Chapter updated successfully'
|
||||
});
|
||||
|
||||
if (data == null) return <Skeleton />;
|
||||
if (identity?.data.user.id !== data.bookChapterOne.book.author.id)
|
||||
throw redirect({ to: `/work/book/${bookId}/overview` });
|
||||
|
||||
return (
|
||||
<BookChapterForm
|
||||
bookId={bookId}
|
||||
initialValues={{
|
||||
title: data.bookChapterOne.title,
|
||||
foreword: data.bookChapterOne.foreword != null ? data.bookChapterOne.foreword : undefined,
|
||||
footnote: data.bookChapterOne.footnote != null ? data.bookChapterOne.footnote : undefined,
|
||||
content: data.bookChapterOne.content
|
||||
}}
|
||||
onChapterSubmit={async chapter => {
|
||||
await chapterUpdate({
|
||||
variables: {
|
||||
input: {
|
||||
id: chapterId,
|
||||
...chapter
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
navigate({ to: `/work/book/${bookId}/overview` });
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const query = gql`
|
||||
query BookChapterEdit($id: ID!) {
|
||||
bookChapterOne(id: $id) {
|
||||
id
|
||||
title
|
||||
foreword
|
||||
footnote
|
||||
content
|
||||
previousChapterId
|
||||
nextChapterId
|
||||
book {
|
||||
id
|
||||
updatedAt
|
||||
wordsTotal
|
||||
chaptersTotal
|
||||
author {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const mutation = gql`
|
||||
mutation BookChapterEditUpdate($input: BookChapterUpdateInput!) {
|
||||
bookChapterUpdate(input: $input) {
|
||||
id
|
||||
title
|
||||
foreword
|
||||
footnote
|
||||
content
|
||||
previousChapterId
|
||||
nextChapterId
|
||||
book {
|
||||
id
|
||||
updatedAt
|
||||
wordsTotal
|
||||
chaptersTotal
|
||||
author {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
160
app/client/pages/work/book/$bookId/chapter/$chapterId/view.tsx
Normal file
160
app/client/pages/work/book/$bookId/chapter/$chapterId/view.tsx
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
import { gql, useQuery } from '@apollo/client';
|
||||
import { EditorSlate } from '@app/client/components/EditorSlate';
|
||||
import { bookChapterFragment } from '@app/client/fragments';
|
||||
import { Button, Heading, Icon, Skeleton, Stack, Text } from '@chakra-ui/react';
|
||||
import { BookChapterViewQuery, BookChapterViewNavigationQuery } from '@generated/types';
|
||||
import { createFileRoute, Link } from '@tanstack/react-router';
|
||||
import { Descendant } from 'slate';
|
||||
|
||||
export const Route = createFileRoute('/work/book/$bookId/chapter/$chapterId/view')({
|
||||
component: RouteComponent
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const { bookId, chapterId } = Route.useParams();
|
||||
|
||||
const { data } = useQuery<BookChapterViewQuery>(bookChapterQuery, {
|
||||
variables: { id: chapterId }
|
||||
});
|
||||
|
||||
const { data: navigationData } = useQuery<BookChapterViewNavigationQuery>(
|
||||
bookChapterNavigationQuery,
|
||||
{
|
||||
variables: { id: chapterId }
|
||||
}
|
||||
);
|
||||
|
||||
if (data == null || navigationData == null) return <Skeleton />;
|
||||
|
||||
return (
|
||||
<Stack justifyContent="center" alignItems="center">
|
||||
<BookChapterView
|
||||
bookId={bookId}
|
||||
chapter={{ ...data.bookChapterOne, ...navigationData.bookChapterOne }}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
interface BookChapterViewChapter {
|
||||
readonly id: string;
|
||||
readonly title: string;
|
||||
readonly createdAt: string;
|
||||
readonly updatedAt: string;
|
||||
readonly foreword?: string | null;
|
||||
readonly footnote?: string | null;
|
||||
readonly content: Descendant[];
|
||||
readonly sequenceNumber: number;
|
||||
readonly reviewsTotal: number;
|
||||
readonly previousChapterId?: string | null;
|
||||
readonly nextChapterId?: string | null;
|
||||
readonly book: {
|
||||
readonly title: string;
|
||||
};
|
||||
}
|
||||
|
||||
function BookChapterView({
|
||||
bookId,
|
||||
chapter
|
||||
}: {
|
||||
readonly bookId: string;
|
||||
readonly chapter: BookChapterViewChapter;
|
||||
}) {
|
||||
return (
|
||||
<Stack
|
||||
maxWidth="840px"
|
||||
width="100%"
|
||||
minHeight="80vh"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
<Heading as="div" maxWidth="100%">
|
||||
<Text textAlign="center" minWidth={0}>
|
||||
{chapter.book.title}
|
||||
</Text>
|
||||
<Text textAlign="center">-</Text>
|
||||
<Text textAlign="center" minWidth={0}>
|
||||
{chapter.title}
|
||||
</Text>
|
||||
</Heading>
|
||||
<EditorSlate initialValue={chapter.content} readOnly />
|
||||
<ChapterNavigation
|
||||
bookId={bookId}
|
||||
previousChapterId={chapter.previousChapterId}
|
||||
nextChapterId={chapter.nextChapterId}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
function ChapterNavigation({
|
||||
bookId,
|
||||
previousChapterId,
|
||||
nextChapterId
|
||||
}: {
|
||||
readonly bookId: string;
|
||||
readonly previousChapterId?: string | null;
|
||||
readonly nextChapterId?: string | null;
|
||||
}) {
|
||||
return (
|
||||
<Stack direction="row" gap={0}>
|
||||
{previousChapterId && (
|
||||
<Link to={`/work/book/${bookId}/chapter/${previousChapterId}/view`}>
|
||||
<Button variant="ghost" borderRightRadius={0}>
|
||||
<Icon>
|
||||
<i className="nf nf-md-page_previous" />
|
||||
</Icon>
|
||||
<Text>Prev</Text>
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
<Link to={`/work/book/${bookId}/overview`}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
borderLeftRadius={previousChapterId ? 0 : undefined}
|
||||
borderRightRadius={nextChapterId ? 0 : undefined}
|
||||
>
|
||||
<Icon>
|
||||
<i className="nf nf-fa-table_list" />
|
||||
</Icon>
|
||||
<Text>Contents</Text>
|
||||
</Button>
|
||||
</Link>
|
||||
{nextChapterId && (
|
||||
<Link to={`/work/book/${bookId}/chapter/${nextChapterId}/view`}>
|
||||
<Button variant="ghost" borderLeftRadius={0}>
|
||||
<Text>Next</Text>
|
||||
<Icon>
|
||||
<i className="nf nf-md-page_next" />
|
||||
</Icon>
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
const bookChapterQuery = gql`
|
||||
query BookChapterView($id: ID!) {
|
||||
bookChapterOne(id: $id) {
|
||||
id
|
||||
content
|
||||
...BookChapter
|
||||
book {
|
||||
title
|
||||
chaptersTotal
|
||||
}
|
||||
}
|
||||
}
|
||||
${bookChapterFragment}
|
||||
`;
|
||||
|
||||
export const bookChapterNavigationQuery = gql`
|
||||
query BookChapterViewNavigation($id: ID!) {
|
||||
bookChapterOne(id: $id) {
|
||||
id
|
||||
previousChapterId
|
||||
nextChapterId
|
||||
}
|
||||
}
|
||||
`;
|
||||
Loading…
Add table
Add a link
Reference in a new issue