article slice

This commit is contained in:
“Salar 2022-10-12 16:34:44 +03:00
parent 43a73bc538
commit 779b091bd5
13 changed files with 264 additions and 0 deletions

View File

@ -0,0 +1,24 @@
import type { ArticleStore } from "../domain/articleStore";
import { getArticleUseCase } from "../useCases/getArticleUseCase";
import { useCallback, useEffect } from "react";
function useArticleViewModel(store: ArticleStore) {
const _getArticle = useCallback(
(id: string) => getArticleUseCase(store.getArticle, store.setArticle, id),
[store.getArticle, store.setArticle]
);
useEffect(() => {
if (store.article != undefined) {
_getArticle(store.article.id);
}
}, [store.article?.id]);
return {
article: store.article,
shouldShowLoading: typeof store.article === "undefined" || store.isLoading,
hasError: store.hasError,
};
}
export { useArticleViewModel };

View File

@ -0,0 +1,35 @@
import axios from "axios";
import { Article } from "../domain/articleEntity";
import { create } from "../domain/articleModel";
import { FetchArticleByIdDTO } from "./dto/fetch_article_by_id_dto";
import Failure from "core/failure";
async function getArticle(id: string): Promise<Article> {
try {
// await new Promise((res, _) => {
// setTimeout(() => res(null), 2000);
// });
const response = await axios.get<FetchArticleByIdDTO>(
`https://run.mocky.io/v3/62cd4581-d864-4d46-b1d6-02b45b5d1994/${id}`
// `https://jsonplaceholder.typicode.com/posts/${id}`
// `http://scipaper.ru/v1/papers/${id}`
);
const dto = response.data;
return create({
id: dto.id,
topic: dto.topic,
title: dto.title,
authors: dto.authors,
tags: dto.tags,
summary: dto.summary,
content: dto.content,
});
} catch (reason) {
if (axios.isAxiosError(reason)) {
throw Failure.fromReason(reason, "failures.services.load");
}
throw reason;
}
}
export { getArticle };

View File

@ -0,0 +1,4 @@
export const SET_ARTICLE = "SET_ARTICLE";
export const GET_ARTICLE = "GET_ARTICLE";
export const GET_ARTICLE_SUCCESS = "GET_ARTICLE.success";
export const GET_ARTICLE_FAILURE = "GET_ARTICLE.failure";

View File

@ -0,0 +1,23 @@
import type { Article } from "../domain/articleEntity";
import { getArticle as getArticleAPI } from "./articleAPIService";
import * as actionTypes from "./articleActionTypes";
import { dispatchStatus } from "../../store/index";
const setArticleAction = (article: Article) => (dispatch: any) =>
dispatch({ type: actionTypes.SET_ARTICLE, article });
const getArticleAction = (id: string) => (dispatch: any) => {
dispatch({ type: actionTypes.GET_ARTICLE });
return getArticleAPI(id)
.then((article) => {
dispatchStatus(actionTypes.GET_ARTICLE, ".success", article)(dispatch);
return article;
})
.catch((reason) => {
dispatchStatus(actionTypes.GET_ARTICLE, ".failure", reason)(dispatch);
return reason;
});
};
export { setArticleAction, getArticleAction };

View File

@ -0,0 +1,39 @@
import React, { useCallback, useState } from "react";
import { useDispatch } from "react-redux";
import { ArticleStore } from "../domain/articleStore";
import type { Article } from "../domain/articleEntity";
import { getArticle as getArticleAPI } from "./articleAPIService";
const useArticleCommonStore = (): ArticleStore => {
const [isLoading, setLoading] = useState<boolean>(false);
const [hasError, setError] = useState<boolean>(false);
const [article, setArticleState] = useState<Article | undefined>();
const dispatch = useDispatch();
const getArticle = useCallback(
async (id: string) => {
setLoading(true);
try {
const article = await getArticleAPI(id);
setArticleState(article);
setLoading(false);
return article;
} catch (error) {
setError(true);
return null;
}
},
[dispatch]
);
return {
article: article,
isLoading,
hasError,
setArticle: setArticleState,
getArticle,
};
};
export { useArticleCommonStore };

View File

@ -0,0 +1,33 @@
import { AnyAction } from "@reduxjs/toolkit";
import { Article } from "article/domain/articleEntity";
import type { ArticleStore } from "../domain/articleStore";
import * as actionTypes from "./articleActionTypes";
type ArticleStoreState = Omit<ArticleStore, "getArticle" | "setArticle">;
const INITIAL_STATE: ArticleStoreState = {
article: undefined,
isLoading: false,
hasError: false,
};
const articleReducer = (
state: ArticleStoreState = INITIAL_STATE,
action: AnyAction
): ArticleStoreState => {
switch (action.type) {
case actionTypes.SET_ARTICLE:
return { ...state, article: action.article };
case actionTypes.GET_ARTICLE:
return { ...state, isLoading: true };
case actionTypes.GET_ARTICLE_SUCCESS:
return { ...state, isLoading: false, article: action.payload };
case actionTypes.GET_ARTICLE_FAILURE:
return { ...state, hasError: true, isLoading: false };
default:
return state;
}
};
export { articleReducer };
export type { ArticleStoreState };

View File

@ -0,0 +1,35 @@
import React, { useCallback, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { ArticleStore } from "../domain/articleStore";
import type { Article } from "../domain/articleEntity";
import type { ArticleStoreState } from "../data/articleReducer";
import { getArticleAction, setArticleAction } from "./articleActions";
import { RootState, useAppSelector } from "store";
const articleSelector = (state: RootState): ArticleStoreState => state.article;
const useArticleStore = (): ArticleStore => {
const { isLoading, article, hasError } = useAppSelector(articleSelector);
const dispatch = useDispatch();
const setArticle = useCallback(
(article: Article) => setArticleAction(article)(dispatch),
[dispatch]
);
const getArticle = useCallback(
(id: string) => getArticleAction(id)(dispatch),
[dispatch]
);
return {
article: article,
isLoading,
hasError,
setArticle,
getArticle,
};
};
export { useArticleStore };

View File

@ -0,0 +1,9 @@
export interface FetchArticleByIdDTO {
id: string;
topic: string;
title: string;
authors: string[];
tags: string[];
summary: string;
content: string;
}

View File

@ -0,0 +1,9 @@
export interface Article {
id: string;
topic: string;
title: string;
authors: string[];
tags: string[];
summary: string;
content: string;
}

View File

@ -0,0 +1,14 @@
import { CreateArticleParams } from "article/useCases/params/create_article_params";
import { Article } from "./articleEntity";
const create = (props: CreateArticleParams): Article => ({
id: props.id,
topic: props.topic,
title: props.title,
authors: props.authors,
tags: props.tags,
summary: props.summary,
content: props.content,
});
export { create };

View File

@ -0,0 +1,14 @@
import type { Article } from "./articleEntity";
interface ArticleStore {
// State
article: Article | undefined;
isLoading: boolean;
hasError: boolean;
// Actions
setArticle(article?: Article): void;
getArticle(identifier: string): Promise<Article | null>;
}
export type { ArticleStore };

View File

@ -0,0 +1,16 @@
import { Article } from "article/domain/articleEntity";
import type { ArticleStore } from "../domain/articleStore";
const getArticleUseCase = async (
getArticle: ArticleStore["getArticle"],
setArticle: ArticleStore["setArticle"],
id: Article["id"]
): Promise<Article | null> => {
const article = await getArticle(id);
if (article) {
await setArticle(article);
}
return article;
};
export { getArticleUseCase };

View File

@ -0,0 +1,9 @@
export interface CreateArticleParams {
id: string;
topic: string;
title: string;
authors: string[];
tags: string[];
summary: string;
content: string;
}