feature/improved-article-content-loading #179
47
Dockerfile
47
Dockerfile
@ -1,30 +1,43 @@
|
|||||||
# Install dependencies only when needed
|
# Install dependencies of project
|
||||||
FROM node:fermium-alpine AS deps
|
FROM node:fermium-alpine AS dependencies
|
||||||
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
|
|
||||||
RUN apk add --no-cache libc6-compat
|
RUN apk add --no-cache libc6-compat
|
||||||
WORKDIR /home/app/
|
WORKDIR /home/app/
|
||||||
COPY package.json package-lock.json ./
|
COPY package.json ./
|
||||||
RUN npm ci
|
RUN npm i
|
||||||
|
|
||||||
|
|
||||||
# Bundle static assets with nginx
|
# Build application to bunch of static files
|
||||||
FROM node:fermium-alpine as production
|
FROM node:fermium-alpine AS builder
|
||||||
|
|
||||||
# Copy built assets from builder
|
|
||||||
WORKDIR /home/app/
|
WORKDIR /home/app/
|
||||||
COPY --from=deps ./home/app/node_modules ./node_modules
|
COPY --from=dependencies ./home/app/node_modules ./node_modules
|
||||||
COPY . .
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
|
||||||
|
# NGINX image
|
||||||
|
FROM nginx:1.21.6 as production
|
||||||
|
# Copy built assets from builder
|
||||||
|
COPY --from=builder /home/app/build /usr/share/nginx/html
|
||||||
|
# Add nginx.config
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
# Copy setup nginx entrypoint file
|
||||||
|
COPY ./scripts/entrypoint.sh .
|
||||||
|
|
||||||
# Expose ports
|
# Expose ports
|
||||||
EXPOSE 3000
|
EXPOSE 80
|
||||||
|
|
||||||
|
# Execute script
|
||||||
|
RUN chmod +x ./entrypoint.sh
|
||||||
|
|
||||||
ENV NODE_ENV production
|
ENV NODE_ENV production
|
||||||
ENV USER_NAME=node_user USER_UID=2000 GROUP_NAME=node_group GROUP_UID=2000
|
# ENV USER_NAME=node_user USER_UID=2000 GROUP_NAME=node_group GROUP_UID=2000
|
||||||
|
|
||||||
RUN deluser --remove-home node \
|
# RUN deluser --remove-home node \
|
||||||
&& addgroup --g ${GROUP_UID} -S ${GROUP_NAME} \
|
# && addgroup --g ${GROUP_UID} -S ${GROUP_NAME} \
|
||||||
&& adduser -D -S -s /sbin/nologin -u ${USER_UID} -G ${GROUP_NAME} ${USER_NAME}
|
# && adduser -D -S -s /sbin/nologin -u ${USER_UID} -G ${GROUP_NAME} ${USER_NAME}
|
||||||
USER "${USER_NAME}"
|
# USER "${USER_NAME}"
|
||||||
|
|
||||||
|
ENTRYPOINT ["./entrypoint.sh"]
|
||||||
|
|
||||||
ENTRYPOINT [ "npm","start"]
|
# Start serving
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
@ -1,6 +1,9 @@
|
|||||||
#! /bin/bash
|
#! /bin/bash
|
||||||
# no verbose
|
# no verbose
|
||||||
set +x
|
set +x
|
||||||
|
# save env to file
|
||||||
|
printenv > .env.production
|
||||||
|
|
||||||
# config
|
# config
|
||||||
envFilename='.env.production'
|
envFilename='.env.production'
|
||||||
resolvingPath='/usr/share/nginx/html'
|
resolvingPath='/usr/share/nginx/html'
|
||||||
|
@ -1,44 +1,30 @@
|
|||||||
import type { ArticleStore } from "../domain/articleStore";
|
import type { ArticleStore } from "../domain/articleStore";
|
||||||
import { useCallback, useEffect } from "react";
|
import { useCallback, useEffect } from "react";
|
||||||
import { getArticle } from "article/data/articleAPIService";
|
|
||||||
import { Article } from "article/domain/articleEntity";
|
import { Article } from "article/domain/articleEntity";
|
||||||
|
import { GetArticleUseCase } from "article/useCases/getArticleUseCase";
|
||||||
|
import { FetchArticleUseCase } from "article/useCases/fetchArticleUseCase";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
|
||||||
function useArticleViewModel(
|
function useArticleViewModel(
|
||||||
store: ArticleStore,
|
store: ArticleStore,
|
||||||
fetchArticleUseCase: (
|
fetchArticleUseCase: FetchArticleUseCase,
|
||||||
fetchArticleCallback: (id: string) => Promise<Article | null>,
|
getArticleUseCase: GetArticleUseCase,
|
||||||
setArticle: ArticleStore["setArticle"],
|
|
||||||
id: string,
|
|
||||||
) => Promise<Article | null>,
|
|
||||||
getArticleUseCase: (
|
|
||||||
getArticle: ArticleStore["getArticle"],
|
|
||||||
setArticle: ArticleStore["setArticle"],
|
|
||||||
id: Article["id"],
|
|
||||||
) => Promise<Article | null>,
|
|
||||||
id: string,
|
|
||||||
) {
|
) {
|
||||||
let article: Article | null | undefined;
|
const { id } = useParams();
|
||||||
|
|
||||||
const _getArticle = useCallback(
|
const getArticle = useCallback(
|
||||||
(id: string) => {
|
() => {
|
||||||
getArticleUseCase(store.getArticle, store.setArticle, id).then(async (value) => {
|
getArticleUseCase.call(id ?? '').catch((_) => fetchArticleUseCase.call(id ?? ''));
|
||||||
if (value == null) {
|
console.log(id);
|
||||||
article = await fetchArticleUseCase(getArticle, store.setArticle, id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
article = value;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[store.setArticle]
|
[id]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(getArticle, []);
|
||||||
_getArticle(id);
|
|
||||||
}, [article]);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
article: store.article,
|
article: store.currentArticle,
|
||||||
shouldShowLoading: typeof store.article === "undefined" || store.isLoading,
|
shouldShowLoading: store.isLoading,
|
||||||
hasError: store.hasError,
|
hasError: store.hasError,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import { integratorApiClient } from "core/httpClient";
|
|||||||
|
|
||||||
const articleEndpoint = "/papers/"
|
const articleEndpoint = "/papers/"
|
||||||
|
|
||||||
async function getArticle(id: string): Promise<Article> {
|
async function fetchArticle(id: string): Promise<Article> {
|
||||||
try {
|
try {
|
||||||
const response = await integratorApiClient.get<FetchArticleByIdDTO>(
|
const response = await integratorApiClient.get<FetchArticleByIdDTO>(
|
||||||
// `https://run.mocky.io/v3/62cd4581-d864-4d46-b1d6-02b45b5d1994/${id}`
|
// `https://run.mocky.io/v3/62cd4581-d864-4d46-b1d6-02b45b5d1994/${id}`
|
||||||
@ -33,4 +33,4 @@ async function getArticle(id: string): Promise<Article> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { getArticle };
|
export { fetchArticle };
|
||||||
|
@ -1,22 +1,25 @@
|
|||||||
import type { Article } from "../domain/articleEntity";
|
import type { Article } from "../domain/articleEntity";
|
||||||
import { getArticle as getArticleAPI } from "./articleAPIService";
|
|
||||||
import * as actionTypes from "./articleActionTypes";
|
import * as actionTypes from "./articleActionTypes";
|
||||||
import { dispatchStatus } from "../../store/index";
|
import { dispatchStatus } from "../../store/index";
|
||||||
|
|
||||||
const setArticleAction = (article: Article) => (dispatch: any) =>
|
const setArticleAction = (article: Article, articlesList: Array<Article>) => (dispatch: any) => {
|
||||||
dispatch({ type: actionTypes.SET_ARTICLE, article });
|
if (!articlesList.includes(article)) {
|
||||||
|
const updatedList = articlesList.concat([article]);
|
||||||
|
dispatch({ type: actionTypes.SET_ARTICLE, article, updatedList });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const getArticleAction = (id: string) => (dispatch: any) => {
|
const getArticleAction = (id: string, articles: Array<Article>) => (dispatch: any) => {
|
||||||
|
const filteredArray = articles.filter((e) => e.id == id);
|
||||||
|
if (filteredArray.length > 0) {
|
||||||
|
const article = filteredArray[0];
|
||||||
|
dispatchStatus(actionTypes.GET_ARTICLE, ".success", article)(dispatch);
|
||||||
|
return article;
|
||||||
|
}
|
||||||
|
|
||||||
return getArticleAPI(id)
|
const reason = 'Article not in the store';
|
||||||
.then((article) => {
|
dispatchStatus(actionTypes.GET_ARTICLE, ".failure", reason)(dispatch);
|
||||||
dispatchStatus(actionTypes.GET_ARTICLE, ".success", article)(dispatch);
|
return null;
|
||||||
return article;
|
|
||||||
})
|
|
||||||
.catch((reason) => {
|
|
||||||
dispatchStatus(actionTypes.GET_ARTICLE, ".failure", reason)(dispatch);
|
|
||||||
return reason;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export { setArticleAction, getArticleAction };
|
export { setArticleAction, getArticleAction };
|
||||||
|
@ -2,37 +2,58 @@ import React, { useCallback, useState } from "react";
|
|||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
import { ArticleStore } from "../domain/articleStore";
|
import { ArticleStore } from "../domain/articleStore";
|
||||||
import type { Article } from "../domain/articleEntity";
|
import type { Article } from "../domain/articleEntity";
|
||||||
import { getArticle as getArticleAPI } from "./articleAPIService";
|
|
||||||
|
|
||||||
const useArticleCommonStore = (): ArticleStore => {
|
const useArticleCommonStore = (): ArticleStore => {
|
||||||
const [isLoading, setLoading] = useState<boolean>(false);
|
const [isLoading, setLoading] = useState<boolean>(false);
|
||||||
const [hasError, setError] = useState<boolean>(false);
|
const [hasError, setError] = useState<boolean>(false);
|
||||||
const [article, setArticleState] = useState<Article | undefined>();
|
const [currentArticle, setCurrentArticle] = useState<Article | null>(null);
|
||||||
|
const [articles, setArticlesState] = useState<Array<Article>>([]);
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const getArticle = useCallback(
|
const getArticle = useCallback(
|
||||||
async (id: string) => {
|
(id: string) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
if (typeof currentArticle === undefined) {
|
||||||
const article = await getArticleAPI(id);
|
const fromStore = findArticleFromStore(id);
|
||||||
setArticleState(article);
|
if (typeof fromStore === undefined) {
|
||||||
|
setError(true);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
return article;
|
setCurrentArticle(fromStore);
|
||||||
} catch (error) {
|
return fromStore;
|
||||||
setError(true);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
setLoading(false);
|
||||||
|
return currentArticle;
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const findArticleFromStore = (id: string): Article | null => {
|
||||||
|
const filteredArray = articles.filter((e) => e.id == id);
|
||||||
|
if (filteredArray.length > 0) {
|
||||||
|
return filteredArray[0];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const setNewArticle = (newArticle: Article) => {
|
||||||
|
setCurrentArticle(newArticle);
|
||||||
|
if (!articles.includes(newArticle)) {
|
||||||
|
setArticlesState(articles.concat([newArticle]));
|
||||||
|
} else if (articles.length == 0) {
|
||||||
|
setArticlesState([newArticle]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
article: article,
|
articles: articles,
|
||||||
|
currentArticle: currentArticle,
|
||||||
isLoading,
|
isLoading,
|
||||||
hasError,
|
hasError,
|
||||||
setArticle: setArticleState,
|
setArticle: setNewArticle,
|
||||||
getArticle,
|
getArticle: getArticle,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5,7 +5,8 @@ import * as actionTypes from "./articleActionTypes";
|
|||||||
type ArticleStoreState = Omit<ArticleStore, "getArticle" | "setArticle">;
|
type ArticleStoreState = Omit<ArticleStore, "getArticle" | "setArticle">;
|
||||||
|
|
||||||
const INITIAL_STATE: ArticleStoreState = {
|
const INITIAL_STATE: ArticleStoreState = {
|
||||||
article: undefined,
|
articles: [],
|
||||||
|
currentArticle: null,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
hasError: false,
|
hasError: false,
|
||||||
};
|
};
|
||||||
@ -16,11 +17,11 @@ const articleReducer = (
|
|||||||
): ArticleStoreState => {
|
): ArticleStoreState => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case actionTypes.SET_ARTICLE:
|
case actionTypes.SET_ARTICLE:
|
||||||
return { ...state, article: action.article };
|
return { ...state, articles: action.updatedList, currentArticle: action.article };
|
||||||
case actionTypes.GET_ARTICLE:
|
case actionTypes.GET_ARTICLE:
|
||||||
return { ...state, isLoading: true };
|
return { ...state, isLoading: true };
|
||||||
case actionTypes.GET_ARTICLE_SUCCESS:
|
case actionTypes.GET_ARTICLE_SUCCESS:
|
||||||
return { ...state, isLoading: false, article: action.payload };
|
return { ...state, isLoading: false, currentArticle: action.payload };
|
||||||
case actionTypes.GET_ARTICLE_FAILURE:
|
case actionTypes.GET_ARTICLE_FAILURE:
|
||||||
return { ...state, hasError: true, isLoading: false };
|
return { ...state, hasError: true, isLoading: false };
|
||||||
default:
|
default:
|
||||||
|
@ -9,22 +9,23 @@ import { RootState, useAppSelector } from "store";
|
|||||||
const articleSelector = (state: RootState): ArticleStoreState => state.article;
|
const articleSelector = (state: RootState): ArticleStoreState => state.article;
|
||||||
|
|
||||||
const useArticleStore = (): ArticleStore => {
|
const useArticleStore = (): ArticleStore => {
|
||||||
const { isLoading, article, hasError } = useAppSelector(articleSelector);
|
const { isLoading, hasError, currentArticle, articles } = useAppSelector(articleSelector);
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const setArticle = useCallback(
|
const setArticle = useCallback(
|
||||||
(article: Article) => setArticleAction(article)(dispatch),
|
(article: Article) => setArticleAction(article, articles)(dispatch),
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
const getArticle = useCallback(
|
const getArticle = useCallback(
|
||||||
(id: string) => getArticleAction(id)(dispatch),
|
(id: string) => getArticleAction(id, articles)(dispatch),
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
article: article,
|
articles: articles,
|
||||||
|
currentArticle: currentArticle,
|
||||||
isLoading,
|
isLoading,
|
||||||
hasError,
|
hasError,
|
||||||
setArticle,
|
setArticle,
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import { Article } from './articleEntity';
|
import { Article } from './articleEntity';
|
||||||
interface ArticleStore {
|
interface ArticleStore {
|
||||||
// State
|
// State
|
||||||
article: Article | undefined;
|
articles: Array<Article>;
|
||||||
|
currentArticle: Article | null;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
hasError: boolean;
|
hasError: boolean;
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
setArticle(article?: Article): void;
|
setArticle(article: Article): void;
|
||||||
getArticle(identifier: string): Promise<Article | null>;
|
getArticle(identifier: string): Article | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type { ArticleStore };
|
export type { ArticleStore };
|
||||||
|
@ -1,6 +1,32 @@
|
|||||||
import { Article } from "article/domain/articleEntity";
|
import { Article } from "article/domain/articleEntity";
|
||||||
import { ArticleStore } from "article/domain/articleStore";
|
import { ArticleStore } from "article/domain/articleStore";
|
||||||
|
|
||||||
|
|
||||||
|
class FetchArticleUseCase {
|
||||||
|
/* ------------------------------ Dependencies ------------------------------ */
|
||||||
|
_fetchArticleCallback: (id: string) => Promise<Article | null>;
|
||||||
|
_store: ArticleStore;
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
constructor(
|
||||||
|
fetchArticle: (id: string) => Promise<Article | null>,
|
||||||
|
store: ArticleStore,
|
||||||
|
) {
|
||||||
|
this._fetchArticleCallback = fetchArticle;
|
||||||
|
this._store = store;
|
||||||
|
}
|
||||||
|
/* ----------------------------- Implementation ----------------------------- */
|
||||||
|
async call(id: string): Promise<Article | null> {
|
||||||
|
return this._fetchArticleCallback(id).then((article) => {
|
||||||
|
if (article != null) {
|
||||||
|
this._store.setArticle(article);
|
||||||
|
}
|
||||||
|
return article;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const fetchArticleUseCase = async (
|
const fetchArticleUseCase = async (
|
||||||
fetchArticleCallback: (id: string) => Promise<Article | null>,
|
fetchArticleCallback: (id: string) => Promise<Article | null>,
|
||||||
setArticle: ArticleStore["setArticle"],
|
setArticle: ArticleStore["setArticle"],
|
||||||
@ -12,5 +38,5 @@ const fetchArticleUseCase = async (
|
|||||||
}
|
}
|
||||||
return article;
|
return article;
|
||||||
};
|
};
|
||||||
|
export { FetchArticleUseCase };
|
||||||
export { fetchArticleUseCase };
|
export { fetchArticleUseCase };
|
||||||
|
@ -1,6 +1,28 @@
|
|||||||
import { Article } from "article/domain/articleEntity";
|
import { Article } from "article/domain/articleEntity";
|
||||||
import type { ArticleStore } from "../domain/articleStore";
|
import type { ArticleStore } from "../domain/articleStore";
|
||||||
|
|
||||||
|
class GetArticleUseCase {
|
||||||
|
/* ------------------------------ Dependencies ------------------------------ */
|
||||||
|
_store: ArticleStore;
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
constructor(
|
||||||
|
store: ArticleStore,
|
||||||
|
) {
|
||||||
|
this._store = store;
|
||||||
|
}
|
||||||
|
/* ----------------------------- Implementation ----------------------------- */
|
||||||
|
async call(id: string): Promise<Article> {
|
||||||
|
const storedArticle = this._store.getArticle(id);
|
||||||
|
if (storedArticle != null) {
|
||||||
|
this._store.setArticle(storedArticle);
|
||||||
|
return storedArticle;
|
||||||
|
}
|
||||||
|
throw new Error('Article not found');
|
||||||
|
}
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const getArticleUseCase = async (
|
const getArticleUseCase = async (
|
||||||
getArticle: ArticleStore["getArticle"],
|
getArticle: ArticleStore["getArticle"],
|
||||||
setArticle: ArticleStore["setArticle"],
|
setArticle: ArticleStore["setArticle"],
|
||||||
@ -13,4 +35,5 @@ const getArticleUseCase = async (
|
|||||||
return article;
|
return article;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export { GetArticleUseCase };
|
||||||
export { getArticleUseCase };
|
export { getArticleUseCase };
|
||||||
|
@ -10,27 +10,20 @@ import { SVGSearch } from "components/icons";
|
|||||||
import BaseLayout from "components/BaseLayout";
|
import BaseLayout from "components/BaseLayout";
|
||||||
import Typography from "components/typography/Typography";
|
import Typography from "components/typography/Typography";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { fetchArticleUseCase } from "article/useCases/fetchArticleUseCase";
|
import { FetchArticleUseCase } from "article/useCases/fetchArticleUseCase";
|
||||||
import { getArticleUseCase } from "article/useCases/getArticleUseCase";
|
import { GetArticleUseCase } from "article/useCases/getArticleUseCase";
|
||||||
|
import { fetchArticle } from "article/data/articleAPIService";
|
||||||
|
|
||||||
const AnArticle = () => {
|
const AnArticle = () => {
|
||||||
const store = useArticleStore();
|
const store = useArticleStore();
|
||||||
const { id } = useParams();
|
|
||||||
const { article, hasError, shouldShowLoading } = useArticleViewModel(
|
const { article, hasError, shouldShowLoading } = useArticleViewModel(
|
||||||
store,
|
store,
|
||||||
fetchArticleUseCase,
|
new FetchArticleUseCase(fetchArticle, store),
|
||||||
getArticleUseCase,
|
new GetArticleUseCase(store),
|
||||||
id ?? '',
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const { i18n, t } = useTranslation();
|
const { i18n, t } = useTranslation();
|
||||||
|
|
||||||
// const { id } = useParams();
|
|
||||||
// const newId = `${id}`;
|
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// store.getArticle(newId);
|
|
||||||
// }, [id]);
|
|
||||||
|
|
||||||
if (hasError) {
|
if (hasError) {
|
||||||
return <NotFound />;
|
return <NotFound />;
|
||||||
}
|
}
|
||||||
|
@ -13,21 +13,17 @@ import BaseLayout from "components/BaseLayout";
|
|||||||
import Container from "components/Container";
|
import Container from "components/Container";
|
||||||
import NotFound from "./NotFound";
|
import NotFound from "./NotFound";
|
||||||
import Markdown from "components/Markdown";
|
import Markdown from "components/Markdown";
|
||||||
import { fetchArticleUseCase } from "article/useCases/fetchArticleUseCase";
|
import { FetchArticleUseCase } from "article/useCases/fetchArticleUseCase";
|
||||||
import { getArticleUseCase } from "article/useCases/getArticleUseCase";
|
import { GetArticleUseCase } from "article/useCases/getArticleUseCase";
|
||||||
|
import { fetchArticle } from "article/data/articleAPIService";
|
||||||
|
|
||||||
const AnArticleBody = () => {
|
const AnArticleBody = () => {
|
||||||
const store = useArticleStore();
|
const store = useArticleStore();
|
||||||
const { id } = useParams();
|
|
||||||
const { article, hasError, shouldShowLoading } = useArticleViewModel(
|
const { article, hasError, shouldShowLoading } = useArticleViewModel(
|
||||||
store,
|
store,
|
||||||
fetchArticleUseCase,
|
new FetchArticleUseCase(fetchArticle, store),
|
||||||
getArticleUseCase,
|
new GetArticleUseCase(store),
|
||||||
id ?? '',
|
|
||||||
);
|
);
|
||||||
// useEffect(() => {
|
|
||||||
// store.getArticle(newId);
|
|
||||||
// }, [id]);
|
|
||||||
if (hasError) <NotFound />;
|
if (hasError) <NotFound />;
|
||||||
return (
|
return (
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user