diff --git a/src/blog/Blog.tsx b/src/blog/Blog.tsx index 19f79e9..3b3e4f3 100644 --- a/src/blog/Blog.tsx +++ b/src/blog/Blog.tsx @@ -15,6 +15,32 @@ import Profile from './components/Profile'; import { useArticles } from './providers/Article'; import { useAuth } from './providers/Author'; import { View, useViewRouter } from "./types/views"; +import { ArticleModel } from "./types/models"; +import { ArticleViewProps, ArticleEditorProps } from "./types/props"; + +function HomeView({ currentUser, open_login, open_profile, open_create, articles, openArticle }: any) { + return ( + <> + + {!currentUser ? ( + + ) : ( + <> + + + + )} + + + + + + ); +} export default function Blog(props: { disableCustomTheme?: boolean }) { const { articles, loading, error } = useArticles(); @@ -27,72 +53,86 @@ export default function Blog(props: { disableCustomTheme?: boolean }) { const { goBack, - openLogin, - openRegister, - openProfile, - openCreate, - openEditor, + navigateToChildren, openArticle, } = useViewRouter(setUI); + type RouterContext = { + ui: any; + articles: ArticleModel[]; + currentUser: any; + openArticle: (index: number) => void; + }; + + type ViewComponentEntry

= { + component: React.ComponentType

; + extraProps?: (ctx: RouterContext) => Partial

; + }; + + const VIEW_COMPONENTS: Record> = { + home: { + component: HomeView, + }, + + login: { + component: Login, + }, + + register: { + component: Register, + }, + + profile: { + component: Profile, + }, + + article: { + component: ArticleView, + extraProps: ({ ui, articles }) => ({ + article: articles[ui.selectedArticle!], + }) satisfies Partial, + }, + + editor: { + component: ArticleEditor, + extraProps: ({ ui, articles }) => ({ + article: ui.selectedArticle !== null ? articles[ui.selectedArticle] : null, + }) satisfies Partial, + }, + + create: { + component: ArticleEditor, + extraProps: () => ({ + article: null, + }) satisfies Partial, + }, + }; + + const renderView = () => { - switch (ui.view) { - case "login": - return goBack(ui.view)} onRegister={openRegister} />; + const entry = VIEW_COMPONENTS[ui.view]; + const ViewComponent = entry.component; - case "register": - return goBack(ui.view)} />; + const childNav = navigateToChildren(ui.view); - case "profile": - return goBack(ui.view)} />; + const ctx: RouterContext = { + ui, + articles, + currentUser, - case "article": - return ( - goBack(ui.view)} - onEdit={openEditor} - /> - ); + openArticle, + }; - case "editor": - return ( - goBack(ui.view)} - /> - ); + const extraProps = entry.extraProps ? entry.extraProps(ctx) : {}; - case "create": - return ( - goBack(ui.view)} - /> - ); - - default: - return ( - <> - - {!currentUser ? ( - - ) : ( - <> - - - - )} - - - - - - ); - } + return ( + goBack(ui.view)} + {...extraProps} + /> + ); }; if (loading) { diff --git a/src/blog/components/Article/ArticleView.tsx b/src/blog/components/Article/ArticleView.tsx index 0823822..47b2a24 100644 --- a/src/blog/components/Article/ArticleView.tsx +++ b/src/blog/components/Article/ArticleView.tsx @@ -5,7 +5,7 @@ import { styled } from '@mui/material/styles'; import ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded'; import EditRoundedIcon from '@mui/icons-material/EditRounded'; import { ArticleMeta } from "../ArticleMeta"; -import { ArticleProps } from '../../types/props'; +import { ArticleViewProps } from '../../types/props'; import {useAuth} from "../../providers/Author"; const ArticleContainer = styled(Box)(({ theme }) => ({ @@ -28,10 +28,12 @@ const CoverImage = styled('img')({ export default function ArticleView({ article, onBack, - onEdit, -}: ArticleProps) { + open_editor, +}: ArticleViewProps) { const { currentUser } = useAuth(); + const onEdit = open_editor; + return ( Promise; // optional async callback } -export interface ArticleProps { +export interface ArticleViewProps { article: ArticleModel; onBack: () => void; - onEdit: () => void; + open_editor?: () => void; // optional because home → article must still work } + export interface ArticleEditorProps { article?: ArticleModel | null; onBack: () => void; diff --git a/src/blog/types/views.ts b/src/blog/types/views.ts index d805787..b36c3de 100644 --- a/src/blog/types/views.ts +++ b/src/blog/types/views.ts @@ -1,4 +1,3 @@ -// types.ts export type View = | "home" | "login" @@ -40,17 +39,16 @@ export const VIEW_TREE: Record = { }, }; -export function useViewRouter(setUI: React.Dispatch) { +export function useViewRouter(setUI: any) { const navigate = (view: View) => { setUI((prev: any) => ({ ...prev, view })); window.scrollTo({ top: 0, behavior: "smooth" }); }; - const goBack = (currentView: View) => { - const parent = VIEW_TREE[currentView].parent; - if (parent) { - setUI((prev: any) => ({ ...prev, view: parent })); - } + // auto back logic from parent + const goBack = (view: View) => { + const parent = VIEW_TREE[view].parent; + if (parent) navigate(parent); }; const openArticle = (i: number) => { @@ -58,14 +56,17 @@ export function useViewRouter(setUI: React.Dispatch) { window.scrollTo({ top: 0, behavior: "smooth" }); }; - return { - navigate, - goBack, - openLogin: () => navigate("login"), - openRegister: () => navigate("register"), - openProfile: () => navigate("profile"), - openCreate: () => navigate("create"), - openEditor: () => navigate("editor"), - openArticle, + // auto child navigators from children[] + const navigateToChildren = (view: View) => { + const node = VIEW_TREE[view]; + const funcs: Record void> = {}; + + node.children?.forEach((child) => { + funcs[`open_${child}`] = () => navigate(child); + }); + + return funcs; }; + + return { navigate, goBack, openArticle, navigateToChildren }; }