radar/twist-v2

View on GitHub
frontend/src/Book/Chapter/index.tsx

Summary

Maintainability
A
1 hr
Test Coverage
import React from "react";
import { Link, RouteComponentProps } from "@reach/router";

import QueryWrapper from "../../QueryWrapper";
import Element from "./Element";
import Footnote from "./Footnote";
import { PreviousChapterLink, NextChapterLink } from "./Link";
import bookLink from "../../Book/link";

import PermissionDenied from "../../PermissionDenied";
import Commit from "../Commit";
import { ChapterQuery, useChapterQuery } from "../../graphql/types";

type ChapterQueryBook = Extract<ChapterQuery["book"], { __typename: "Book" }>;

type LatestCommit = ChapterQueryBook["latestCommit"];

export type ChapterAtCommitProps = RouteComponentProps &
  ChapterQueryBook["commit"] & {
    bookTitle: string;
    bookPermalink: string;
    gitRef: string;
    latestCommit: LatestCommit;
  };

export const chapterPositionAndTitle = (
  part: string,
  position: number,
  title: string
) => {
  if (part === "mainmatter") {
    return `${position}. ${title}`;
  } else {
    return title;
  }
};

const ChapterAtCommit: React.FC<ChapterAtCommitProps> = ({
  // Params
  bookPermalink,
  bookTitle,
  gitRef,
  // Commit props
  sha,
  createdAt,
  branch,
  // Latest commit props
  latestCommit,
  // Chapter props
  chapter,
}) => {
  const { previousChapter, nextChapter } = chapter;

  const renderPreviousChapterLink = () => {
    if (previousChapter) {
      return (
        <PreviousChapterLink
          {...previousChapter}
          bookPermalink={bookPermalink}
          gitRef={gitRef}
        />
      );
    }
  };

  const renderNextChapterLink = () => {
    if (nextChapter) {
      return (
        <NextChapterLink
          {...nextChapter}
          gitRef={gitRef}
          bookPermalink={bookPermalink}
        />
      );
    }
  };

  const renderChapterLinks = () => {
    return (
      <div>
        <div className="flex">
          <div>{renderPreviousChapterLink()}</div>
          <div className="ml-auto">{renderNextChapterLink()}</div>
        </div>
      </div>
    );
  };

  const {
    part,
    position,
    title: chapterTitle,
    elements,
    footnotes,
  } = chapter;

  const renderFootnotes = () => {
    return (
      <div>
        <h3>Footnotes</h3>
        {footnotes.map((footnote) => (
          <Footnote {...footnote} key={footnote.identifier} />
        ))}
      </div>
    );
  };

  const positionAndTitle = chapterPositionAndTitle(
    part,
    position,
    chapterTitle
  );

  const bookPath = bookLink(bookPermalink, gitRef);

  const commit = { sha, createdAt, branch };

  return (
    <div className="flex flex-wrap lg:flex-no-wrap mx-16 md:mx-0">
      <div className="w-1/12"></div>
      <div className="w-full lg:w-3/4 flex-grow mr-4 chapter">
        <header className="mb-4">
          <h1>
            <Link id="top" to={bookPath}>
              {bookTitle}
            </Link>
          </h1>

          <Commit
            permalink={bookPermalink}
            commit={commit}
            latestCommit={latestCommit}
          />
        </header>
        {renderChapterLinks()}
        <h2 className="mt-3">{positionAndTitle}</h2>
        {elements &&
          elements.map((element) => (
            <Element
              {...element}
              bookPermalink={bookPermalink}
              key={element.id}
            />
          ))}

        {renderFootnotes()}
        {renderChapterLinks()}
      </div>
    </div>
  );
};

interface WrappedChapterMatchParams {
  bookPermalink: string;
  chapterPermalink: string;
  gitRef: string;
}

const WrappedChapter: React.FC<
  RouteComponentProps<WrappedChapterMatchParams>
> = ({ bookPermalink, chapterPermalink, gitRef }) => {
  const { data, loading, error } = useChapterQuery({
    variables: {
      bookPermalink: bookPermalink as string,
      chapterPermalink: chapterPermalink as string,
      gitRef: gitRef,
    },
  });

  const renderChapter = (data: ChapterQuery) => {
    const { book } = data;

    if (book.__typename === "PermissionDenied") {
      return <PermissionDenied />;
    }

    return (
      <ChapterAtCommit
        {...book.commit}
        bookTitle={book.title}
        bookPermalink={bookPermalink as string}
        gitRef={gitRef as string}
        latestCommit={book.latestCommit}
      />
    );
  };

  return (
    <QueryWrapper loading={loading} error={error}>
      {data && renderChapter(data)}
    </QueryWrapper>
  );
};
export { ChapterAtCommit };
export default WrappedChapter;