From b7d23068e6e5bfff6e022a0d11bc4b16e661beeb Mon Sep 17 00:00:00 2001 From: ohakutsu Date: Wed, 12 Jul 2023 15:26:05 +0900 Subject: [PATCH] Support slide mode --- README.md | 7 +- src/client/components/Article.tsx | 32 +++---- src/client/components/ArticleInfo.tsx | 3 + .../components/QiitaMarkdownHtmlBody.tsx | 30 +++++++ src/client/components/Slide/SlideViewer.tsx | 71 +++++++++++++++ .../components/Slide/SlideViewerContent.tsx | 57 ++++++++++++ .../components/Slide/SlideViewerDashboard.tsx | 87 +++++++++++++++++++ .../Slide/SlideViewerDashboardNavigation.tsx | 55 ++++++++++++ .../Slide/SlideViewerDashboardQiitaLogo.tsx | 9 ++ .../Slide/SlideViewerDashboardTooltip.tsx | 15 ++++ src/client/components/Slide/escape.ts | 15 ++++ .../Slide/get-magnitude-from-range.ts | 8 ++ src/client/components/Slide/index.tsx | 20 +++++ src/client/components/Slide/slide-pages.ts | 14 +++ src/client/pages/items/show.tsx | 8 +- src/commands/publish.ts | 2 + src/lib/check-frontmatter-type.test.ts | 36 ++++++++ src/lib/check-frontmatter-type.ts | 10 +++ src/lib/entities/qiita-item.ts | 4 + src/lib/file-system-repo.test.ts | 2 + src/lib/file-system-repo.ts | 15 +++- src/lib/view-models/items.ts | 1 + src/qiita-api/index.ts | 7 ++ src/server/api/items.ts | 3 + 24 files changed, 481 insertions(+), 30 deletions(-) create mode 100644 src/client/components/QiitaMarkdownHtmlBody.tsx create mode 100644 src/client/components/Slide/SlideViewer.tsx create mode 100644 src/client/components/Slide/SlideViewerContent.tsx create mode 100644 src/client/components/Slide/SlideViewerDashboard.tsx create mode 100644 src/client/components/Slide/SlideViewerDashboardNavigation.tsx create mode 100644 src/client/components/Slide/SlideViewerDashboardQiitaLogo.tsx create mode 100644 src/client/components/Slide/SlideViewerDashboardTooltip.tsx create mode 100644 src/client/components/Slide/escape.ts create mode 100644 src/client/components/Slide/get-magnitude-from-range.ts create mode 100644 src/client/components/Slide/index.tsx create mode 100644 src/client/components/Slide/slide-pages.ts diff --git a/README.md b/README.md index 790c5f9..cce0302 100644 --- a/README.md +++ b/README.md @@ -18,12 +18,6 @@ Qiita の Markdown 記法については[Markdown 記法 チートシート](htt Qiita CLI、Qiita Preview は現在ベータ版です。 機能についても開発中のものがあります。 -未実装の機能は以下の通りです。 - -- スライドモードのプレビュー - -これらの機能に関しましては、正式版リリースまでに開発を行っていきます。 -正式リリースまでは破壊的なアップデートなどが頻繁にされる可能性がございますのでご了承ください。 ## Qiita CLI の導入方法について @@ -147,6 +141,7 @@ private: false # true: 限定共有記事 / false: 公開記事 updated_at: "" # 記事を投稿した際に自動的に記事の更新日時に変わります id: null # 記事を投稿した際に自動的に記事のUUIDに変わります organization_url_name: null # 関連付けるOrganizationのURL名 +slide: false # true: スライドモードON / false: スライドモードOFF --- # new article body ``` diff --git a/src/client/components/Article.tsx b/src/client/components/Article.tsx index 94af8a1..3ef343d 100644 --- a/src/client/components/Article.tsx +++ b/src/client/components/Article.tsx @@ -1,38 +1,25 @@ import { css } from "@emotion/react"; +import { useRef } from "react"; import { Colors, - getSpace, LineHeight, Typography, Weight, + getSpace, } from "../lib/variables"; import { MaterialSymbol } from "./MaterialSymbol"; -import { useState, useEffect, useRef } from "react"; -import { - applyMathJax, - executeScriptTagsInElement, -} from "../lib/embed-init-scripts"; +import { QiitaMarkdownHtmlBody } from "./QiitaMarkdownHtmlBody"; +import { Slide } from "./Slide"; interface Props { renderedBody: string; tags: string[]; title: string; + slide: boolean; } -export const Article = ({ renderedBody, tags, title }: Props) => { - const bodyElement = useRef(null); - const [isRendered, setIsRendered] = useState(false); - - useEffect(() => { - if (isRendered) { - bodyElement.current && executeScriptTagsInElement(bodyElement.current); - bodyElement.current && applyMathJax(bodyElement.current); - } - }, [isRendered, bodyElement, renderedBody]); - - useEffect(() => { - setIsRendered(true); - }, []); +export const Article = ({ renderedBody, tags, title, slide }: Props) => { + const bodyRef = useRef(null); return (
@@ -50,8 +37,9 @@ export const Article = ({ renderedBody, tags, title }: Props) => { ))} -
-
+
+ {slide && } +
); diff --git a/src/client/components/ArticleInfo.tsx b/src/client/components/ArticleInfo.tsx index 1ec098f..d3e6a59 100644 --- a/src/client/components/ArticleInfo.tsx +++ b/src/client/components/ArticleInfo.tsx @@ -17,6 +17,7 @@ interface Props { published: boolean; errorMessages: string[]; qiitaItemUrl: string | null; + slide: boolean; } export const ArticleInfo = ({ @@ -26,6 +27,7 @@ export const ArticleInfo = ({ published, errorMessages, qiitaItemUrl, + slide, }: Props) => { const [isOpen, setIsOpen] = useState( localStorage.getItem("openInfoState") === "true" ? true : false @@ -84,6 +86,7 @@ export const ArticleInfo = ({ {organizationUrlName || "紐付けなし"} + {slide ? "ON" : "OFF"} {errorMessages.length > 0 && (
diff --git a/src/client/components/QiitaMarkdownHtmlBody.tsx b/src/client/components/QiitaMarkdownHtmlBody.tsx new file mode 100644 index 0000000..4317a65 --- /dev/null +++ b/src/client/components/QiitaMarkdownHtmlBody.tsx @@ -0,0 +1,30 @@ +import { RefObject, useEffect, useState } from "react"; +import { + applyMathJax, + executeScriptTagsInElement, +} from "../lib/embed-init-scripts"; + +export const QiitaMarkdownHtmlBody = ({ + renderedBody, + bodyRef, +}: { + renderedBody: string; + bodyRef: RefObject; +}) => { + const [isRendered, setIsRendered] = useState(false); + + useEffect(() => { + setIsRendered(true); + }, []); + + useEffect(() => { + if (isRendered && bodyRef.current) { + executeScriptTagsInElement(bodyRef.current); + applyMathJax(bodyRef.current); + } + }, [isRendered, bodyRef, renderedBody]); + + return ( +
+ ); +}; diff --git a/src/client/components/Slide/SlideViewer.tsx b/src/client/components/Slide/SlideViewer.tsx new file mode 100644 index 0000000..41a6274 --- /dev/null +++ b/src/client/components/Slide/SlideViewer.tsx @@ -0,0 +1,71 @@ +import { useCallback, useEffect, useRef, useState } from "react"; +import { SlideViewerContent } from "./SlideViewerContent"; +import { SlideViewerDashboard } from "./SlideViewerDashboard"; + +const LEFT_KEY = 37; +const RIGHT_KEY = 39; + +export const SlideViewer = ({ pages }: { pages: string[] }) => { + const [isFullScreen, setIsFullScreen] = useState(false); + + const contentRef = useRef(null); + const scrollToTopOfContent = useCallback(() => { + if (contentRef.current) { + contentRef.current.scrollTop = 0; + } + }, []); + + const [currentPageIndex, setCurrentPageIndex] = useState(0); + const next = useCallback(() => { + if (currentPageIndex + 1 < pages.length) { + setCurrentPageIndex(currentPageIndex + 1); + scrollToTopOfContent(); + } + }, [currentPageIndex, pages.length, scrollToTopOfContent]); + const prev = useCallback(() => { + if (currentPageIndex - 1 >= 0) { + setCurrentPageIndex(currentPageIndex - 1); + scrollToTopOfContent(); + } + }, [currentPageIndex, scrollToTopOfContent]); + + useEffect(() => { + const handleGlobalKeyDown = (event: KeyboardEvent) => { + if (event.keyCode === LEFT_KEY) { + prev(); + } else if (event.keyCode === RIGHT_KEY) { + next(); + } + }; + + window.addEventListener("keydown", handleGlobalKeyDown); + + return () => { + window.removeEventListener("keydown", handleGlobalKeyDown); + }; + }, [next, prev]); + + return ( +
+
+ +
+ + setCurrentPageIndex(page - 1)} + onSwitchFullScreen={() => setIsFullScreen(!isFullScreen)} + /> +
+ ); +}; diff --git a/src/client/components/Slide/SlideViewerContent.tsx b/src/client/components/Slide/SlideViewerContent.tsx new file mode 100644 index 0000000..21c1957 --- /dev/null +++ b/src/client/components/Slide/SlideViewerContent.tsx @@ -0,0 +1,57 @@ +import classNames from "classnames"; +import { MouseEvent as ReactMouseEvent, RefObject, useCallback } from "react"; +import { QiitaMarkdownHtmlBody } from "../QiitaMarkdownHtmlBody"; + +export const SlideViewerContent = ({ + pages, + currentPageIndex, + onPrevious, + onNext, + contentRef, +}: { + pages: string[]; + currentPageIndex: number; + onPrevious: () => void; + onNext: () => void; + contentRef: RefObject; +}) => { + const handleClickScreen = useCallback< + (event: ReactMouseEvent) => void + >( + (event) => { + const clickedElement = event.target as HTMLElement; + + // If a viewer clicks or element, we don't navigate. + if (clickedElement.tagName === "IMG" || clickedElement.tagName === "A") { + return; + } + + // We want to use getBoundingClientRect because it always returns + // the actual rendered element dimensions, even if there are CSS + // transformations applied to it. + const rect = event.currentTarget.getBoundingClientRect(); + + // Should we transition to the next or the previous slide? + if (event.clientX - rect.left > rect.width / 2) { + onNext(); + } else { + onPrevious(); + } + }, + [onPrevious, onNext] + ); + + return ( +
+ +
+ ); +}; diff --git a/src/client/components/Slide/SlideViewerDashboard.tsx b/src/client/components/Slide/SlideViewerDashboard.tsx new file mode 100644 index 0000000..84a5038 --- /dev/null +++ b/src/client/components/Slide/SlideViewerDashboard.tsx @@ -0,0 +1,87 @@ +import { useState } from "react"; +import { MaterialSymbol } from "../MaterialSymbol"; +import { getMagnitudeFromRange } from "./get-magnitude-from-range"; +import { SlideViewerDashboardNavigation } from "./SlideViewerDashboardNavigation"; +import { SlideViewerDashboardQiitaLogo } from "./SlideViewerDashboardQiitaLogo"; +import { SlideViewerDashboardTooltip } from "./SlideViewerDashboardTooltip"; + +export const SlideViewerDashboard = ({ + currentPage, + totalPage, + isFullScreen, + onPrevious, + onNext, + onSwitchFullScreen, + onSetPage, +}: { + currentPage: number; + totalPage: number; + isFullScreen: boolean; + onPrevious: () => void; + onNext: () => void; + onSwitchFullScreen: () => void; + onSetPage: (page: number) => void; +}) => { + const [isTooltipVisible, setIsTooltipVisible] = useState(false); + const [destinationPage, setDestinationPage] = useState(currentPage); + const [tooltipLeftDistance, setTooltipLeftDistance] = useState(0); + + return ( +
+ {isTooltipVisible && ( + + {destinationPage}/{totalPage} + + )} + + + + + {currentPage} / {totalPage} + + +
{ + setIsTooltipVisible(true); + setTooltipLeftDistance( + event.clientX - event.currentTarget.getBoundingClientRect().left + ); + setDestinationPage( + getMagnitudeFromRange(event.currentTarget, event.clientX, totalPage) + ); + }} + onMouseLeave={() => { + setIsTooltipVisible(false); + }} + onClick={() => { + onSetPage(destinationPage); + }} + > +
+
+ + + + +
+ ); +}; diff --git a/src/client/components/Slide/SlideViewerDashboardNavigation.tsx b/src/client/components/Slide/SlideViewerDashboardNavigation.tsx new file mode 100644 index 0000000..5f8b046 --- /dev/null +++ b/src/client/components/Slide/SlideViewerDashboardNavigation.tsx @@ -0,0 +1,55 @@ +import classNames from "classnames"; +import { Fragment } from "react"; +import { MaterialSymbol } from "../MaterialSymbol"; + +export const SlideViewerDashboardNavigation = ({ + currentPage, + totalPage, + onPrevious, + onNext, +}: { + currentPage: number; + totalPage: number; + onPrevious: () => void; + onNext: () => void; +}) => { + const disablePrevious = currentPage === 1; + const disableNext = currentPage === totalPage; + + return ( + + + + + ); +}; diff --git a/src/client/components/Slide/SlideViewerDashboardQiitaLogo.tsx b/src/client/components/Slide/SlideViewerDashboardQiitaLogo.tsx new file mode 100644 index 0000000..ce83bf2 --- /dev/null +++ b/src/client/components/Slide/SlideViewerDashboardQiitaLogo.tsx @@ -0,0 +1,9 @@ +export const SlideViewerDashboardQiitaLogo = () => ( + + Qiita + + + + + +); diff --git a/src/client/components/Slide/SlideViewerDashboardTooltip.tsx b/src/client/components/Slide/SlideViewerDashboardTooltip.tsx new file mode 100644 index 0000000..f220efb --- /dev/null +++ b/src/client/components/Slide/SlideViewerDashboardTooltip.tsx @@ -0,0 +1,15 @@ +import { ReactNode } from "react"; + +export const SlideViewerDashboardTooltip = ({ + leftDistance, + children, +}: { + leftDistance: number; + children: ReactNode; +}) => { + return ( +
+ {children} +
+ ); +}; diff --git a/src/client/components/Slide/escape.ts b/src/client/components/Slide/escape.ts new file mode 100644 index 0000000..f81cf54 --- /dev/null +++ b/src/client/components/Slide/escape.ts @@ -0,0 +1,15 @@ +const REGEXP = /[&'`"<>]/g; + +const map = { + "&": "&", + "<": "<", + ">": ">", + '"': """, + "'": "'", + "`": "`", +}; + +const replace = (match: keyof typeof map) => map[match]; + +export const escape = (html: string): string => + html.replace(REGEXP, replace as any); // eslint-disable-line @typescript-eslint/no-explicit-any diff --git a/src/client/components/Slide/get-magnitude-from-range.ts b/src/client/components/Slide/get-magnitude-from-range.ts new file mode 100644 index 0000000..1ac89f9 --- /dev/null +++ b/src/client/components/Slide/get-magnitude-from-range.ts @@ -0,0 +1,8 @@ +export function getMagnitudeFromRange( + target: Element, + x: number, + length: number +) { + const rect = target.getBoundingClientRect(); + return Math.ceil((x - rect.left) / (rect.width / length)); +} diff --git a/src/client/components/Slide/index.tsx b/src/client/components/Slide/index.tsx new file mode 100644 index 0000000..e08ed06 --- /dev/null +++ b/src/client/components/Slide/index.tsx @@ -0,0 +1,20 @@ +import { SlideViewer } from "./SlideViewer"; +import { slidePages } from "./slide-pages"; + +export const Slide = ({ + renderedBody, + title, +}: { + renderedBody: string; + title: string; +}) => { + const pages = slidePages({ + title, + author: { + urlName: "", + }, + body: renderedBody, + }); + + return ; +}; diff --git a/src/client/components/Slide/slide-pages.ts b/src/client/components/Slide/slide-pages.ts new file mode 100644 index 0000000..e24beff --- /dev/null +++ b/src/client/components/Slide/slide-pages.ts @@ -0,0 +1,14 @@ +import { escape } from "./escape"; + +export const slidePages = (article: { + title: string; + author: { urlName: string }; + body: string; +}): string[] => [ + `

${escape( + article.title + )}

by ${ + article.author.urlName + }
`, + ...article.body.split(//), +]; diff --git a/src/client/pages/items/show.tsx b/src/client/pages/items/show.tsx index 96afdfd..16bbef7 100644 --- a/src/client/pages/items/show.tsx +++ b/src/client/pages/items/show.tsx @@ -97,12 +97,14 @@ export const ItemsShow = () => { published={item.published} errorMessages={item.error_messages} qiitaItemUrl={item.qiita_item_url} + slide={item.slide} />
@@ -120,7 +122,7 @@ export const ItemsShow = () => { error - {errorMessage} +
{errorMessage}

))}
@@ -180,3 +182,7 @@ const errorStyle = css({ display: "flex", marginTop: getSpace(3 / 2), }); + +const errorMessageBodyStyle = css({ + whiteSpace: "pre", +}); diff --git a/src/commands/publish.ts b/src/commands/publish.ts index c97fd3b..2a8d90e 100644 --- a/src/commands/publish.ts +++ b/src/commands/publish.ts @@ -75,6 +75,7 @@ export const publish = async (argv: string[]) => { uuid: item.id, isPrivate: item.secret, organizationUrlName: item.organizationUrlName, + slide: item.slide, }); console.log(`Updated: ${item.name} -> ${item.id}`); @@ -85,6 +86,7 @@ export const publish = async (argv: string[]) => { title: item.title, isPrivate: item.secret, organizationUrlName: item.organizationUrlName, + slide: item.slide, }); fileSystemRepo.updateItemUuid(item.name, responseItem.id); diff --git a/src/lib/check-frontmatter-type.test.ts b/src/lib/check-frontmatter-type.test.ts index 8ab9bbe..80eb0f6 100644 --- a/src/lib/check-frontmatter-type.test.ts +++ b/src/lib/check-frontmatter-type.test.ts @@ -8,6 +8,7 @@ describe("checkFrontmatterType", () => { updatedAt: "", id: null, organizationUrlName: null, + slide: false, }; it("returns no errors", () => { @@ -127,4 +128,39 @@ describe("checkFrontmatterType", () => { }); }); }); + + describe("checkFrontmatterTypeSlide", () => { + describe("when slide is String", () => { + const errorMessages = checkFrontmatterType({ + ...frontMatter, + slide: "true" as unknown as boolean, + }); + + it("returns errors", () => { + expect(errorMessages.length).toEqual(1); + }); + }); + + describe("when slide is true", () => { + const errorMessages = checkFrontmatterType({ + ...frontMatter, + slide: true, + }); + + it("returns no errors", () => { + expect(errorMessages).toEqual([]); + }); + }); + + describe("when slide is false", () => { + const errorMessages = checkFrontmatterType({ + ...frontMatter, + slide: false, + }); + + it("returns no errors", () => { + expect(errorMessages).toEqual([]); + }); + }); + }); }); diff --git a/src/lib/check-frontmatter-type.ts b/src/lib/check-frontmatter-type.ts index 66db5e6..2f98e39 100644 --- a/src/lib/check-frontmatter-type.ts +++ b/src/lib/check-frontmatter-type.ts @@ -5,6 +5,7 @@ interface FrontMatter { updatedAt: string | null; id: string | null; organizationUrlName: string | null; + slide: boolean; } interface CheckType { @@ -20,6 +21,7 @@ export const checkFrontmatterType = (frontMatter: FrontMatter): string[] => { checkUpdatedAt, checkId, checkOrganizationUrlName, + checkSlide, ]; return getErrorMessages(frontMatter, checkFrontMatterTypes); }; @@ -61,6 +63,14 @@ const checkOrganizationUrlName: CheckType = { }, }; +const checkSlide: CheckType = { + getMessage: () => + "slideの設定はtrue/falseで入力してください\n\n【破壊的な変更がありました】\n詳しくは以下のリリースをご確認ください\nhttps://github.com/increments/qiita-cli/releases/tag/v0.5.0", + isValid: ({ slide }) => { + return typeof slide === "boolean"; + }, +}; + const checkId: CheckType = { getMessage: () => "idは文字列で入力してください", isValid: ({ id }) => { diff --git a/src/lib/entities/qiita-item.ts b/src/lib/entities/qiita-item.ts index d250aa4..3a94364 100644 --- a/src/lib/entities/qiita-item.ts +++ b/src/lib/entities/qiita-item.ts @@ -11,6 +11,7 @@ export class QiitaItem { public readonly itemsShowPath: string; public readonly published: boolean; public readonly itemPath: string; + public readonly slide: boolean; constructor({ id, @@ -25,6 +26,7 @@ export class QiitaItem { itemsShowPath, published, itemPath, + slide, }: { id: string | null; title: string; @@ -38,6 +40,7 @@ export class QiitaItem { itemsShowPath: string; published: boolean; itemPath: string; + slide: boolean; }) { this.id = id; this.title = title; @@ -51,5 +54,6 @@ export class QiitaItem { this.itemsShowPath = itemsShowPath; this.published = published; this.itemPath = itemPath; + this.slide = slide; } } diff --git a/src/lib/file-system-repo.test.ts b/src/lib/file-system-repo.test.ts index 1dbc532..a0bcfaf 100644 --- a/src/lib/file-system-repo.test.ts +++ b/src/lib/file-system-repo.test.ts @@ -185,6 +185,7 @@ tags: [] content: `--- id: id_${suffix} tags: [] +slide: false ---`, }; }; @@ -295,6 +296,7 @@ tags: [] created_at: "", updated_at: "", organization_url_name: null, + slide: false, }; const remoteFilename = `${id}.md`; const localFilename = remoteFilename; diff --git a/src/lib/file-system-repo.ts b/src/lib/file-system-repo.ts index 9b4a015..2dc85c1 100644 --- a/src/lib/file-system-repo.ts +++ b/src/lib/file-system-repo.ts @@ -13,6 +13,7 @@ class FileContent { public readonly id: string | null; public readonly organizationUrlName: string | null; public readonly rawBody: string; + public readonly slide: boolean; constructor({ title, @@ -22,6 +23,7 @@ class FileContent { id, organizationUrlName, rawBody, + slide, }: { title: string; tags: string[]; @@ -30,6 +32,7 @@ class FileContent { id: string | null; organizationUrlName: string | null; rawBody: string; + slide: boolean; }) { this.title = title; this.tags = tags; @@ -38,6 +41,7 @@ class FileContent { this.id = id; this.organizationUrlName = organizationUrlName; this.rawBody = rawBody; + this.slide = slide; } static read(fileContent: string): FileContent { @@ -50,6 +54,7 @@ class FileContent { updatedAt: data.updated_at, id: data.id, organizationUrlName: data.organization_url_name, + slide: data.slide, }); } @@ -68,6 +73,7 @@ class FileContent { updatedAt: "", id, organizationUrlName: null, + slide: false, }); } @@ -80,6 +86,7 @@ class FileContent { updatedAt: item.updated_at, id: item.id, organizationUrlName: item.organization_url_name, + slide: item.slide, }); } @@ -92,6 +99,7 @@ class FileContent { updatedAt: item.updatedAt, id: item.id, organizationUrlName: item.organizationUrlName, + slide: item.slide, }); } @@ -103,6 +111,7 @@ class FileContent { updated_at: this.updatedAt, id: this.id, organization_url_name: this.organizationUrlName, + slide: this.slide, }); } @@ -121,7 +130,8 @@ class FileContent { this.title === aFileContent.title && this.tags.sort().join() === aFileContent.tags.sort().join() && this.secret === aFileContent.secret && - this.rawBody === aFileContent.rawBody + this.rawBody === aFileContent.rawBody && + this.slide === aFileContent.slide ); } @@ -134,6 +144,7 @@ class FileContent { id, organizationUrlName: this.organizationUrlName, rawBody: this.rawBody, + slide: this.slide, }); } } @@ -339,6 +350,7 @@ export class FileSystemRepo { updatedAt: localFileContent.updatedAt, organizationUrlName: localFileContent.organizationUrlName, rawBody: localFileContent.rawBody, + slide: localFileContent.slide, name: basename, modified: !localFileContent.equals(remoteFileContent), itemsShowPath: this.generateItemsShowPath(localFileContent.id, basename), @@ -373,6 +385,7 @@ export class FileSystemRepo { updatedAt: localFileContent.updatedAt, organizationUrlName: localFileContent.organizationUrlName, rawBody: localFileContent.rawBody, + slide: localFileContent.slide, name: basename, modified: !localFileContent.equals(remoteFileContent), itemsShowPath: this.generateItemsShowPath(localFileContent.id, basename), diff --git a/src/lib/view-models/items.ts b/src/lib/view-models/items.ts index ad84eb4..b2a968f 100644 --- a/src/lib/view-models/items.ts +++ b/src/lib/view-models/items.ts @@ -22,6 +22,7 @@ export type ItemsShowViewModel = { qiita_item_url: string | null; rendered_body: string; secret: boolean; + slide: boolean; tags: string[]; title: string; }; diff --git a/src/qiita-api/index.ts b/src/qiita-api/index.ts index 1187b95..7571661 100644 --- a/src/qiita-api/index.ts +++ b/src/qiita-api/index.ts @@ -13,6 +13,7 @@ export interface Item { coediting: boolean; created_at: string; updated_at: string; + slide: boolean; } export class QiitaApi { @@ -188,12 +189,14 @@ export class QiitaApi { title, isPrivate, organizationUrlName, + slide, }: { rawBody: string; tags: string[]; title: string; isPrivate: boolean; organizationUrlName: string | null; + slide: boolean; }) { const data = JSON.stringify({ body: rawBody, @@ -206,6 +209,7 @@ export class QiitaApi { }), private: isPrivate, organization_url_name: organizationUrlName, + slide, }); const path = `/api/v2/items`; @@ -222,6 +226,7 @@ export class QiitaApi { tags, isPrivate, organizationUrlName, + slide, }: { uuid: string; rawBody: string; @@ -229,6 +234,7 @@ export class QiitaApi { tags: string[]; isPrivate: boolean; organizationUrlName: string | null; + slide: boolean; }) { const data = JSON.stringify({ body: rawBody, @@ -241,6 +247,7 @@ export class QiitaApi { }), private: isPrivate, organization_url_name: organizationUrlName, + slide, }); const path = `/api/v2/items/${uuid}`; diff --git a/src/server/api/items.ts b/src/server/api/items.ts index dd99f30..00f16a8 100644 --- a/src/server/api/items.ts +++ b/src/server/api/items.ts @@ -102,6 +102,7 @@ const itemsShow = async (req: Express.Request, res: Express.Response) => { published, qiita_item_url: qiitaItemUrl, rendered_body: renderedBody, + slide: item.slide, tags: item.tags, title: item.title, }; @@ -149,6 +150,7 @@ const itemsUpdate = async (req: Express.Request, res: Express.Response) => { title: result.title, isPrivate: result.secret, organizationUrlName: result.organizationUrlName, + slide: result.slide, }); if (item) { fileSystemRepo.updateItemUuid(basename, item.id); @@ -162,6 +164,7 @@ const itemsUpdate = async (req: Express.Request, res: Express.Response) => { uuid: result.id, isPrivate: result.secret, organizationUrlName: result.organizationUrlName, + slide: result.slide, }); } else { throw new Error("Unknown Error");