From 464f482e8fe12c44b37d3a196e1456078c425eb9 Mon Sep 17 00:00:00 2001 From: calliope Date: Wed, 28 Sep 2022 23:00:44 +0900 Subject: [PATCH 1/8] divide api wrapper --- src/utils/api/auth/index.ts | 53 +++++++++++++++++++++++++++++++++++++ src/utils/api/blog/index.ts | 36 +++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 src/utils/api/auth/index.ts create mode 100644 src/utils/api/blog/index.ts diff --git a/src/utils/api/auth/index.ts b/src/utils/api/auth/index.ts new file mode 100644 index 0000000..4131f4f --- /dev/null +++ b/src/utils/api/auth/index.ts @@ -0,0 +1,53 @@ +import type { ContactContentType } from '#src/components/SelfIntroduction/ContactForm'; +import type { Blog, Category, User } from '#src/types'; + +import axios, { AxiosResponse } from 'axios'; + +const BACKEND_ORIGIN = process.env.NEXT_PUBLIC_BACKEND_ORIGIN; + +const axiosWithCredentials = axios.create({ + withCredentials: true, +}); + +export async function adminLogin(userData: User) { + const REQUEST_URL = new URL('/api/admin/login/', BACKEND_ORIGIN).href; + const response: AxiosResponse = await axiosWithCredentials.post( + REQUEST_URL, + userData, + ); + return response; +} + +export async function adminVerifyTokens() { + const REQUEST_URL = new URL('/api/admin/tokens/verify/', BACKEND_ORIGIN) + .href; + const response: AxiosResponse = await axiosWithCredentials.post( + REQUEST_URL, + ); + return response; +} + +export async function adminPostBlog(blog: Blog) { + const REQUEST_URL = new URL('/api/admin/blogs/post/', BACKEND_ORIGIN).href; + const response: AxiosResponse = await axiosWithCredentials.post( + REQUEST_URL, + blog, + ); + return response; +} + +export async function adminPostCategory(category: Category) { + const REQUEST_URL = new URL('/api/admin/categories/post/', BACKEND_ORIGIN) + .href; + const response: AxiosResponse = await axiosWithCredentials.post( + REQUEST_URL, + category, + ); + return response; +} + +export async function postContactForm(content: ContactContentType) { + const REQUEST_URL = new URL('/api/contact-form/', BACKEND_ORIGIN).href; + const response: AxiosResponse = await axios.post(REQUEST_URL, content); + return response.status; +} diff --git a/src/utils/api/blog/index.ts b/src/utils/api/blog/index.ts new file mode 100644 index 0000000..8ffc802 --- /dev/null +++ b/src/utils/api/blog/index.ts @@ -0,0 +1,36 @@ +import axios, { AxiosResponse } from 'axios'; + +import type { Blog, Category } from '#src/types'; + +const BACKEND_ORIGIN = process.env.NEXT_PUBLIC_BACKEND_ORIGIN; + +const axiosWithCredentials = axios.create({ + withCredentials: true, +}); + +export async function fetchBlogList(onlyPublished = true) { + const REQUEST_URL = onlyPublished + ? new URL('/api/blogs/', BACKEND_ORIGIN).href + : new URL('/api/admin/blogs/', BACKEND_ORIGIN).href; + const response: AxiosResponse = await axiosWithCredentials.get( + REQUEST_URL, + ); + const blogList = response.data.sort((a, b) => b.updated_at - a.updated_at); + return blogList; +} + +export async function fetchBlog(id: string) { + const REQUEST_URL = new URL(`/api/blogs/${id}`, BACKEND_ORIGIN).href; + const response: AxiosResponse = await axios.get(REQUEST_URL); + return response.data; +} + +export async function fetchCategories() { + const REQUEST_URL = new URL('/api/categories/', BACKEND_ORIGIN).href; + const response: AxiosResponse = await axios.get(REQUEST_URL); + const categoryList = response.data; + categoryList.forEach((value) => { + value.key = Number(value.key); + }); + return categoryList; +} From e32d1c048b979175991a148e3e575953cd2658df Mon Sep 17 00:00:00 2001 From: calliope Date: Wed, 28 Sep 2022 23:01:02 +0900 Subject: [PATCH 2/8] update --- src/components/BlogDetail/BlogNotFound.tsx | 28 ---- src/components/BlogDetail/index.tsx | 43 ++--- src/components/BlogList/BlogListItem.tsx | 154 ++++++++---------- src/components/BlogList/index.tsx | 43 ++--- src/components/Editor/AddCategoryModal.tsx | 2 +- src/components/Editor/MarkdownEditor.tsx | 2 +- src/components/Editor/MetaDataEditor.tsx | 3 +- src/components/Footer/index.tsx | 2 +- src/components/LoginForm/index.tsx | 6 +- src/components/MarkdownView/CodeContainer.tsx | 6 +- src/components/Navbar/index.tsx | 4 +- .../SelfIntroduction/ContactForm/index.tsx | 2 +- .../SkillList/CertificationsList.tsx | 3 - .../SkillList/ProgrammingSkillList.tsx | 2 +- src/components/SelfIntroduction/index.tsx | 2 +- src/components/ShareButtons/index.tsx | 4 +- src/layouts/client.tsx | 2 +- src/pages/_app.tsx | 1 + src/pages/admin/[uuid].tsx | 4 +- src/pages/admin/index.tsx | 4 +- src/pages/blogs/[uuid].tsx | 39 ++--- src/pages/blogs/index.tsx | 17 +- src/pages/index.tsx | 2 +- src/utils/backendApi.ts | 71 -------- src/utils/hooks.ts | 6 +- 25 files changed, 160 insertions(+), 292 deletions(-) delete mode 100644 src/components/BlogDetail/BlogNotFound.tsx delete mode 100644 src/utils/backendApi.ts diff --git a/src/components/BlogDetail/BlogNotFound.tsx b/src/components/BlogDetail/BlogNotFound.tsx deleted file mode 100644 index 5308990..0000000 --- a/src/components/BlogDetail/BlogNotFound.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { useRouter } from 'next/router'; -import { useEffect, useState } from 'react'; -import { Box, Typography } from '@mui/material'; - -export const BlogNotFound: React.FC<{ redirectUrl?: string }> = ({ - redirectUrl = '/', -}) => { - const router = useRouter(); - const [time, setTime] = useState(10); - useEffect(() => { - // eslint-disable-next-line @typescript-eslint/no-misused-promises - const timer = setInterval(async () => { - setTime((prev) => Math.max(prev - 1, 0)); - if (time === 0) { - await router.replace(redirectUrl); - } - }, 1000); - return () => { - clearInterval(timer); - }; - }, [time, router, redirectUrl]); - return ( - - Not Found... - {time}秒後にリダイレクトします - - ); -}; diff --git a/src/components/BlogDetail/index.tsx b/src/components/BlogDetail/index.tsx index c78229c..f4759ef 100644 --- a/src/components/BlogDetail/index.tsx +++ b/src/components/BlogDetail/index.tsx @@ -2,61 +2,42 @@ import dayjs from 'dayjs'; import Head from 'next/head'; import { Box, Container, Divider, Typography } from '@mui/material'; -import { Loader, MarkdownView, ShareButtons } from '#src/components'; -import { useBlogListState } from '#src/utils/hooks'; +import { MarkdownView, ShareButtons } from '#src/components'; import { BlogIndex } from './BlogIndex'; -import { BlogNotFound } from './BlogNotFound'; - -export const BlogDetail: React.FC<{ uuid: string }> = ({ uuid }) => { - const { data } = useBlogListState(); - if (!data) { - return ; - } - const blogData = data.find((blog) => blog.uuid === uuid); - if (!blogData) { - return ( - <> - - ブログが存在しません - - - - - - - ); - } +import { Blog } from '#src/types'; + +export const BlogDetail: React.FC<{ blog: Blog }> = ({ blog }) => { return ( <> - {blogData.title} - + {blog.title} + - {blogData.title} - {blogData.sub_title} + {blog.title} + {blog.sub_title} 作成日時:  - {dayjs.unix(blogData.created_at).format('YYYY年MM月DD日')} + {dayjs.unix(blog.created_at).format('YYYY年MM月DD日')} 更新日時:  - {dayjs.unix(blogData.updated_at).format('YYYY年MM月DD日')} + {dayjs.unix(blog.updated_at).format('YYYY年MM月DD日')} - + - + ); diff --git a/src/components/BlogList/BlogListItem.tsx b/src/components/BlogList/BlogListItem.tsx index d90e155..715f1e5 100644 --- a/src/components/BlogList/BlogListItem.tsx +++ b/src/components/BlogList/BlogListItem.tsx @@ -6,98 +6,86 @@ import Link from 'next/link'; import path from 'path'; import { useState } from 'react'; import { - Box, - Card, - Chip, - Container, - Link as MuiLink, - Skeleton, - Stack, + Box, + Card, + Chip, + Container, + Link as MuiLink, + Skeleton, + Stack, } from '@mui/material'; import { COLORS } from '#src/styles'; export const BlogListItem: React.FC<{ - blogData?: Blog; - categoryData?: Category[]; -}> = ({ blogData, categoryData }) => { - const [isHovering, setIsHovering] = useState(false); + blog: Blog; + categoryData?: Category[]; +}> = ({ blog, categoryData }) => { + const [isHovering, setIsHovering] = useState(false); - function getBlogUrl(uuid: string) { - return path.join('/blogs/', uuid); - } + function getBlogUrl(uuid: string) { + return path.join('/blogs/', uuid); + } - return ( - { - setIsHovering(true); - }} - onMouseEnter={() => { - setIsHovering(true); - }} - onMouseLeave={() => setIsHovering(false)} - > - - - {blogData ? ( - - - {blogData.title} - - - ) : ( - - )} - + return ( + { + setIsHovering(true); + }} + onMouseEnter={() => { + setIsHovering(true); + }} + onMouseLeave={() => setIsHovering(false)} + > + + + + + {blog.title} + + + - - {blogData?.sub_title || } - + + {blog.sub_title} + - - {blogData ? ( - markdownToTxt(blogData.content) - ) : ( - - )} - + + {markdownToTxt(blog.content)} + - - {blogData && categoryData ? ( - blogData.categories.map((val) => ( - - )) - ) : ( - - )} - + + {blog && categoryData ? ( + blog.categories.map((val) => ( + + )) + ) : ( + + )} + - - 最終更新:  - {blogData ? ( - dayjs.unix(blogData.updated_at).format('YYYY年MM月DD日') - ) : ( - - )} - - - - ); + + 最終更新:  + {dayjs.unix(blog.updated_at).format('YYYY年MM月DD日')} + + + + ); }; diff --git a/src/components/BlogList/index.tsx b/src/components/BlogList/index.tsx index aaf3894..cc3c096 100644 --- a/src/components/BlogList/index.tsx +++ b/src/components/BlogList/index.tsx @@ -1,16 +1,14 @@ -import lodash from 'lodash'; import { useEffect, useState } from 'react'; import { useInView } from 'react-intersection-observer'; import { Box, Container, Stack, Typography } from '@mui/material'; import { BlogListItem } from './BlogListItem'; -import { useBlogListState, useCategoriesState } from '#src/utils/hooks'; +import { useCategoriesState } from '#src/utils/hooks'; +import { Blog } from '#src/types'; +import { Loader } from '#src/components'; // ブログ一覧 -export const BlogList: React.FC = () => { - // 全blogデータ - const { data, error } = useBlogListState(); - +export const BlogList: React.FC<{ blogs: Blog[] }> = ({ blogs }) => { // 全カテゴリーデータ const { data: categoryData } = useCategoriesState(); @@ -20,43 +18,34 @@ export const BlogList: React.FC = () => { // 参照と参照先が画面内に表示されているか // 表示されたらブログを追加表示 const [ref, isDisplayed] = useInView(); + const LOAD_BLOG_COUNT = 10 useEffect(() => { if (isDisplayed) { - setBlogDisplay((prev) => prev + 10); + setBlogDisplay((prev) => prev + LOAD_BLOG_COUNT); } }, [isDisplayed]); return ( {/* ブログ一覧 */} - - {data ? ( - data - .slice(0, blogsDisplay) - .map((blogData) => ( - - )) - ) : ( - <> - {lodash.range(blogsDisplay).map((val) => ( - - ))} - - )} + + {blogs.slice(0, blogsDisplay).map((blogData) => ( + + ))} {/* 無限スクロール */} - {blogsDisplay >= (data?.length || 0) ? ( + {blogsDisplay >= (blogs.length || 0) ? ( No More Contents... ) : ( - + )} diff --git a/src/components/Editor/AddCategoryModal.tsx b/src/components/Editor/AddCategoryModal.tsx index afef61a..e5a84af 100644 --- a/src/components/Editor/AddCategoryModal.tsx +++ b/src/components/Editor/AddCategoryModal.tsx @@ -13,7 +13,7 @@ import { Typography, } from '@mui/material'; -import { adminPostCategory } from '#src/utils/backendApi'; +import { adminPostCategory } from '#src/utils/api/auth'; // Categoryを追加するためのモーダル export const AddCategoryModal: React.FC = () => { diff --git a/src/components/Editor/MarkdownEditor.tsx b/src/components/Editor/MarkdownEditor.tsx index 6ab386c..55ad9e4 100644 --- a/src/components/Editor/MarkdownEditor.tsx +++ b/src/components/Editor/MarkdownEditor.tsx @@ -4,7 +4,7 @@ import { useState, useContext, useRef } from 'react'; import { Alert, Box, Button, Snackbar, TextareaAutosize } from '@mui/material'; import { blogDataContext } from './contexts'; -import { adminPostBlog } from '#src/utils/backendApi'; +import { adminPostBlog } from '#src/utils/api/auth'; export const MarkdownEditor: React.FC = () => { // saveされたか diff --git a/src/components/Editor/MetaDataEditor.tsx b/src/components/Editor/MetaDataEditor.tsx index bcec210..5269320 100644 --- a/src/components/Editor/MetaDataEditor.tsx +++ b/src/components/Editor/MetaDataEditor.tsx @@ -19,7 +19,7 @@ import { } from '@mui/lab'; import AdapterDayjs from '@mui/lab/AdapterDayjs'; -import { adminPostBlog } from '#src/utils/backendApi'; +import { adminPostBlog } from '#src/utils/api/auth'; import { useCategoriesState } from '#src/utils/hooks'; import { AddCategoryModal } from './AddCategoryModal'; import { blogDataContext, BlogDataContextType } from './contexts'; @@ -49,6 +49,7 @@ export const MetaDataEditor: React.FC = () => { data.content = blogData.blogDataContextValue.content; data.created_at = dayjs(data.created_at).unix(); data.updated_at = dayjs(data.updated_at).unix(); + console.log(data); await adminPostBlog(data); blogData.setBlogDataContextValue(() => data); diff --git a/src/components/Footer/index.tsx b/src/components/Footer/index.tsx index 512ddb2..778ff11 100644 --- a/src/components/Footer/index.tsx +++ b/src/components/Footer/index.tsx @@ -1,7 +1,7 @@ import dayjs from 'dayjs'; import { Box, Container } from '@mui/material'; -import { COLORS } from '../../styles'; +import { COLORS } from '#src/styles'; export const Footer: React.FC = () => { return ( diff --git a/src/components/LoginForm/index.tsx b/src/components/LoginForm/index.tsx index 01e157d..03f8fff 100644 --- a/src/components/LoginForm/index.tsx +++ b/src/components/LoginForm/index.tsx @@ -1,12 +1,12 @@ -import type { User } from '../../types'; +import type { User } from '#src/types'; import assert from 'assert'; import { useForm } from 'react-hook-form'; import { useSetRecoilState } from 'recoil'; import { FormControl, InputLabel, Input, Button, Box } from '@mui/material'; -import { isAuthenticatedState } from '../../atoms/authAtom'; -import { adminLogin } from '../../utils/backendApi'; +import { isAuthenticatedState } from '#src/atoms/authAtom'; +import { adminLogin } from '#src/utils/api/auth'; export const LoginForm: React.FC = () => { const setIsAuthed = useSetRecoilState(isAuthenticatedState); diff --git a/src/components/MarkdownView/CodeContainer.tsx b/src/components/MarkdownView/CodeContainer.tsx index f8d8603..759fd21 100644 --- a/src/components/MarkdownView/CodeContainer.tsx +++ b/src/components/MarkdownView/CodeContainer.tsx @@ -3,9 +3,9 @@ import { Box, IconButton, Tooltip, Typography } from '@mui/material'; import LightModeIcon from '@mui/icons-material/LightMode'; import ContentCopyIcon from '@mui/icons-material/ContentCopy'; -import { isDarkState } from '../../atoms/codeStyleAtom'; -import { COLORS } from '../../styles'; -import { useCurrentAbsUrl } from '../../utils/hooks'; +import { isDarkState } from '#src/atoms/codeStyleAtom'; +import { COLORS } from '#src/styles'; +import { useCurrentAbsUrl } from '#src/utils/hooks'; const FILE_EXTENSIONS: { [lang: string]: string } = { python: '.py', diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx index 934ec45..57e83fc 100644 --- a/src/components/Navbar/index.tsx +++ b/src/components/Navbar/index.tsx @@ -1,10 +1,10 @@ -import type { navItem } from '../../types'; +import type { navItem } from '#src/types'; import Link from 'next/link'; import { AppBar, Box, Container, Link as MuiLink, Stack } from '@mui/material'; import { NavItem } from './NavItem'; -import { COLORS } from '../../styles'; +import { COLORS } from '#src/styles'; // 画面上部のナビゲーションバー export const Navbar: React.FC = () => { diff --git a/src/components/SelfIntroduction/ContactForm/index.tsx b/src/components/SelfIntroduction/ContactForm/index.tsx index 144b543..044cdf3 100644 --- a/src/components/SelfIntroduction/ContactForm/index.tsx +++ b/src/components/SelfIntroduction/ContactForm/index.tsx @@ -3,7 +3,7 @@ import { Box, Button, TextField } from '@mui/material'; import SendIcon from '@mui/icons-material/Send'; import { COLORS } from '#src/styles'; -import { postContactForm } from '#src/utils/backendApi'; +import { postContactForm } from '#src/utils/api/auth'; export type ContactContentType = { subject?: string; diff --git a/src/components/SelfIntroduction/SkillList/CertificationsList.tsx b/src/components/SelfIntroduction/SkillList/CertificationsList.tsx index ddcb044..fbcbc2f 100644 --- a/src/components/SelfIntroduction/SkillList/CertificationsList.tsx +++ b/src/components/SelfIntroduction/SkillList/CertificationsList.tsx @@ -19,9 +19,6 @@ export const CertificationsList: React.FC = () => { HSK 5級 - - 応用情報技術者試験(AP) 勉強中 - E資格 勉強中 diff --git a/src/components/SelfIntroduction/SkillList/ProgrammingSkillList.tsx b/src/components/SelfIntroduction/SkillList/ProgrammingSkillList.tsx index 8b10319..a03b922 100644 --- a/src/components/SelfIntroduction/SkillList/ProgrammingSkillList.tsx +++ b/src/components/SelfIntroduction/SkillList/ProgrammingSkillList.tsx @@ -24,7 +24,7 @@ import { SvelteIcon, TypeScriptIcon, VueIcon, -} from '../../Icons'; +} from '#src/components'; export const ProgrammingSkillList: React.FC = () => { const [openPython, setOpenPython] = useState(false); diff --git a/src/components/SelfIntroduction/index.tsx b/src/components/SelfIntroduction/index.tsx index fc62ef3..36df8d3 100644 --- a/src/components/SelfIntroduction/index.tsx +++ b/src/components/SelfIntroduction/index.tsx @@ -1,6 +1,6 @@ import { Box, Container, Stack, Typography } from '@mui/material'; -import { SkillList } from '#src/components/SelfIntroduction/SkillList'; +import { SkillList } from '#src/components'; import { ContactForm } from './ContactForm'; export const SelfIntroduction: React.FC = () => { diff --git a/src/components/ShareButtons/index.tsx b/src/components/ShareButtons/index.tsx index b9e3e0b..17da562 100644 --- a/src/components/ShareButtons/index.tsx +++ b/src/components/ShareButtons/index.tsx @@ -6,8 +6,8 @@ import ShareIcon from '@mui/icons-material/Share'; import TwitterIcon from '@mui/icons-material/Twitter'; import { LineIcon } from '..'; -import { COLORS } from '../../styles'; -import { useCurrentAbsUrl } from '../../utils/hooks'; +import { COLORS } from '#src/styles'; +import { useCurrentAbsUrl } from '#src/utils/hooks'; export const ShareButtons: React.FC = () => { const [isClicked, setIsClicked] = useState(false); diff --git a/src/layouts/client.tsx b/src/layouts/client.tsx index 7010be7..4049f4e 100644 --- a/src/layouts/client.tsx +++ b/src/layouts/client.tsx @@ -24,6 +24,7 @@ export const ClientLayout: React.FC = ({ }} />