finished types; novel improvement; fixed navbar for mobile

This commit is contained in:
2020-10-12 14:05:43 +03:00
parent 41e2869fa3
commit a2b629a850
17 changed files with 197 additions and 115 deletions
+9 -9
View File
@@ -3944,9 +3944,9 @@
} }
}, },
"caniuse-lite": { "caniuse-lite": {
"version": "1.0.30001146", "version": "1.0.30001147",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001146.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001147.tgz",
"integrity": "sha512-VAy5RHDfTJhpxnDdp2n40GPPLp3KqNrXz1QqFv4J64HvArKs8nuNMOWkB3ICOaBTU/Aj4rYAo/ytdQDDFF/Pug==", "integrity": "sha512-CPyN875geYk46eIqPl5jlmotCr5YZC2KxIVfb4z0FrNfLxPM+MyodWD2irJGDG8vUUE1fmg3De9vt8uaC6Nf6w==",
"dev": true "dev": true
}, },
"capture-exit": { "capture-exit": {
@@ -4791,9 +4791,9 @@
} }
}, },
"css-what": { "css-what": {
"version": "3.4.1", "version": "3.4.2",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.1.tgz", "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz",
"integrity": "sha512-wHOppVDKl4vTAOWzJt5Ek37Sgd9qq1Bmj/T1OjvicWbU5W7ru7Pqbn0Jdqii3Drx/h+dixHKXNhZYx7blthL7g==", "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==",
"dev": true "dev": true
}, },
"cssdb": { "cssdb": {
@@ -13914,9 +13914,9 @@
}, },
"dependencies": { "dependencies": {
"ajv": { "ajv": {
"version": "6.12.5", "version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true, "dev": true,
"requires": { "requires": {
"fast-deep-equal": "^3.1.1", "fast-deep-equal": "^3.1.1",
+26 -9
View File
@@ -1,6 +1,6 @@
const baseUrl = process.env.NODE_ENV === "development" ? 'http://192.168.0.103:8080/v1' : 'https://api.unmei.space/v1'; const baseUrl = process.env.NODE_ENV === "development" ? 'http://localhost:8080/v1' : 'https://api.unmei.space/v1';
const request: ApiRequest = async (url: string, method: string, body?: string | FormData) => { async function request<T>(url: string, method: string, body?: string | FormData): Promise<T> {
let bUrl = baseUrl; let bUrl = baseUrl;
if(baseUrl.endsWith('/')) bUrl = bUrl.slice(0, baseUrl.length-1); if(baseUrl.endsWith('/')) bUrl = bUrl.slice(0, baseUrl.length-1);
if(url.startsWith('/')) url = url.slice(1, baseUrl.length); if(url.startsWith('/')) url = url.slice(1, baseUrl.length);
@@ -10,7 +10,7 @@ const request: ApiRequest = async (url: string, method: string, body?: string |
credentials: "include" credentials: "include"
}); });
let json: ApiResponse; let json: ApiResponse<T>;
try { try {
json = await res.json(); json = await res.json();
} catch (e) { } catch (e) {
@@ -22,13 +22,22 @@ const request: ApiRequest = async (url: string, method: string, body?: string |
}); });
if(json?.error) return Promise.reject(json.error_data); if(json?.error) return Promise.reject(json.error_data);
if(!res.ok) return Promise.reject(res.statusText); if(!res.ok) return Promise.reject(res.statusText);
if(!json.data) return Promise.reject(null);
return Promise.resolve(json.data); return Promise.resolve(json.data);
} }
const get: Get = (url: string, data?: object) => request(url, 'GET', JSON.stringify(data)); function get<T>(url: string, data?: object): Promise<T> {
const post: Post = (url: string, data?: object | FormData) => request(url, 'POST', data instanceof FormData ? data : JSON.stringify(data)); return request<T>(url, 'GET', JSON.stringify(data));
const put: Put = (url: string, data?: object) => request(url, 'PUT', JSON.stringify(data)); }
const del: Delete = (url: string, data?: object) => request(url, 'DELETE', JSON.stringify(data)); function post<T>(url: string, data?: object | FormData): Promise<T> {
return request(url, 'POST', data instanceof FormData ? data : JSON.stringify(data));
}
function put<T>(url: string, data?: object): Promise<T> {
return request<T>(url, 'PUT', JSON.stringify(data));
}
function del<T>(url: string, data?: object): Promise<T> {
return request<T>(url, 'DELETE', JSON.stringify(data));
}
const TranslateStatus: { [id: string]: string } = { const TranslateStatus: { [id: string]: string } = {
planned: 'Запланировано', planned: 'Запланировано',
@@ -38,7 +47,15 @@ const TranslateStatus: { [id: string]: string } = {
deferred: 'Отложено' deferred: 'Отложено'
}; };
export const getVersion = () => get('version'); const TranslatePlatform: { [id: string]: string } = {
win: 'Windows'
};
const TranslateExitStatus: { [id: string]: string } = {
came_out: 'Вышла'
}
export const getVersion = () => get<VersionResponse>('version');
export const version = '0.10b'; export const version = '0.10b';
export { get, post, put, del, TranslateStatus }; export { get, post, put, del, TranslateStatus, TranslatePlatform, TranslateExitStatus };
+4 -4
View File
@@ -1,6 +1,6 @@
import {post} from "./api"; import {post} from "./api";
export const restore: Restore = (token: string, new_password: string) => post('auth/restore', { token, new_password }); export const restore = (token: string, new_password: string) => post<string>('auth/restore', { token, new_password });
export const registerUser: Register = (login: string, password: string, email: string, recaptcha: string) => post('auth/register', { login, password, email, recaptcha }); export const registerUser = (login: string, password: string, email: string, recaptcha: string) => post<UserType>('auth/register', { login, password, email, recaptcha });
export const login: Login = (login: string, password: string, recaptcha: string) => post('auth/login', { login, password, recaptcha }); export const login = (login: string, password: string, recaptcha: string) => post<UserType>('auth/login', { login, password, recaptcha });
export const userLogout: Logout = () => post('auth/logout'); export const userLogout = () => post<string>('auth/logout');
+2 -2
View File
@@ -1,4 +1,4 @@
import {get} from "./api"; import {get} from "./api";
export const fetchChar: FetchChar = (id: number) => get(`characters/${id}`); export const fetchChar = (id: number) => get<CharacterType>(`characters/${id}`);
export const fetchCharNovels: FetchCharNovels = (id: number) => get(`characters/${id}/novels`); export const fetchCharNovels = (id: number) => get<NovelType[]>(`characters/${id}/novels`);
+4 -4
View File
@@ -1,7 +1,7 @@
import {del, get, put} from './api'; import {del, get, put} from './api';
export const fetchNews = () => get('news'); export const fetchNews = () => get<PostType[]>('news');
export const fetchPost = (id: number) => get(`news/${id}`); export const fetchPost = (id: number) => get<PostType>(`news/${id}`);
export const updatePost = (post: PostType) => put(`news/${post.id}`, post); export const updatePost = (post: PostType) => put<PostType>(`news/${post.id}`, post);
export const deletePost = (id: number) => del(`news/${id}`); export const deletePost = (id: number) => del(`news/${id}`);
+12 -12
View File
@@ -1,20 +1,20 @@
import { get, post, put, del } from "./api"; import { get, post, put, del } from "./api";
export const fetchNovels = (orderBy?: string) => get(orderBy ? `novels?sort=${orderBy}` : 'novels'); export const fetchNovels = (orderBy?: string) => get<NovelType[]>(orderBy ? `novels?sort=${orderBy}` : 'novels');
export const createNovel = (novel: NovelType) => post('novels', { ...novel }); export const createNovel = (novel: NovelType) => post<NovelType>('novels', { ...novel });
export const fetchNovel = (id: number) => get(`novels/${id}`); export const fetchNovel = (id: number) => get<NovelType>(`novels/${id}`);
export const updateNovel = (id: number, novel: NovelType) => put(`novels/${id}`, { ...novel }); export const updateNovel = (id: number, novel: NovelType) => put<NovelType>(`novels/${id}`, { ...novel });
export const fetchNovelCharacters = (id: number) => get(`novels/${id}/characters`); export const fetchNovelCharacters = (id: number) => get<CharacterType[]>(`novels/${id}/characters`);
export const fetchNovelGenres = (id: number) => get(`novels/${id}/genres`); export const fetchNovelGenres = (id: number) => get<GenreType[]>(`novels/${id}/genres`);
export const fetchNovelComments = (id: number, offset=0, count=5) => get(`novels/${id}/comments?offset=${offset}&count=${count}`); export const fetchNovelComments = (id: number, offset=0, count=5) => get<CommentsResponse>(`novels/${id}/comments?offset=${offset}&count=${count}`);
export const postNovelComment = (id: number, text: string) => post(`novels/${id}/comments`, { text }); export const postNovelComment = (id: number, text: string) => post<CommentType>(`novels/${id}/comments`, { text });
export const fetchUserNovel = (userId: number, novelId: number) => get(`users/${userId}/novels/${novelId}`); export const fetchUserNovel = (userId: number, novelId: number) => get<UserNovelType>(`users/${userId}/novels/${novelId}`);
export const createUserNovel = (userId: number, novelId: number, status='planned') => post(`users/${userId}/novels`, { novel_id: Number(novelId), status }); export const createUserNovel = (userId: number, novelId: number, status='planned') => post<UserNovelType>(`users/${userId}/novels`, { novel_id: Number(novelId), status });
export const updateUserNovel = (userId: number, novelId: number, data: { mark?: number; status?: string }) => put(`users/${userId}/novels/${novelId}`, data); export const updateUserNovel = (userId: number, novelId: number, data: { mark?: number; status?: string }) => put<UserNovelType>(`users/${userId}/novels/${novelId}`, data);
export const deleteUserNovel = (userId: number, novelId: number) => del(`users/${userId}/novels/${novelId}`); export const deleteUserNovel = (userId: number, novelId: number) => del(`users/${userId}/novels/${novelId}`);
+11 -11
View File
@@ -1,19 +1,19 @@
import {get, post, put} from "./api"; import {get, post, put} from "./api";
export const fetchUsers: FetchUsers = () => get('users'); export const fetchUsers = () => get<UserType[]>('users');
export const fetchUser: FetchUser = (id: number) => get(`users/${id}`); export const fetchUser = (id: number) => get<UserType>(`users/${id}`);
export const updateUser: UpdateUser = (user: UserType) => put(`users/${user.id}`, { user }); export const updateUser = (user: UserType) => put<UserType>(`users/${user.id}`, { user });
export const fetchCurrentUser: FetchCurrentUser = () => get('users/me'); export const fetchCurrentUser = () => get<UserType>('users/me');
export const fetchUserNovels: FetchUserNovels = (id: number) => get(`users/${id}/novels`); export const fetchUserNovels = (id: number) => get<NovelType[]>(`users/${id}/novels`);
export const activateAccount: ActivateAccount = (token: string) => post('auth/activate', { token }); export const activateAccount = (token: string) => post<string>('auth/activate', { token });
export const generateActivateLink: GenerateActivateLink = () => post('auth/activateToken'); export const generateActivateLink = () => post<string>('auth/activateToken');
export const fetchUserSettings: FetchUserSettings = () => get('users/me/settings'); export const fetchUserSettings = () => get<UserSettingsType>('users/me/settings');
export const updateUserGeneralSettings: UpdateUserGeneralSettings = (use_gravatar: boolean, avatar: string) => post('users/me/settings', { use_gravatar, avatar }); export const updateUserGeneralSettings = (use_gravatar: boolean, avatar: string) => post<UserType>('users/me/settings', { use_gravatar, avatar });
export const updateUserAppearanceSettings: UpdateUserAppearanceSettings = (theme: string) => post('users/me/settings', { theme }); export const updateUserAppearanceSettings = (theme: string) => post<UserType>('users/me/settings', { theme });
export const uploadAvatar = (avatar: FormData) => post('users/me/avatar', avatar); export const uploadAvatar = (avatar: FormData) => post<string>('users/me/avatar', avatar);
export const generateRestoreLink = (email: string, recaptcha: string) => post('auth/restoreToken', { email, recaptcha }); export const generateRestoreLink = (email: string, recaptcha: string) => post('auth/restoreToken', { email, recaptcha });
+12
View File
@@ -0,0 +1,12 @@
import React from "react";
// @ts-ignore
import Loader from 'react-loader-spinner';
import Popout from "../../ui/Modal/Popout";
const LoadingModal = () => (
<Popout>
<Loader color='#aaa' type='Rings'/>
</Popout>
);
export default LoadingModal;
+10 -2
View File
@@ -56,17 +56,25 @@ $hoverColor: #f22
cursor: pointer cursor: pointer
&:hover>&_Menu &:hover>&_Menu
display: block display: flex
flex-direction: column
&_Menu &_Menu
display: none display: none
position: absolute position: absolute
background-color: #fefefe background-color: var(--nav-bg-color)
box-sizing: border-box box-sizing: border-box
padding: .5rem padding: .5rem
max-width: 250px
right: 0
&_Item &_Item
margin: .2rem 0 margin: .2rem 0
color: var(--main-color)
&:hover
color: var(--link-color)
&__User &__User
justify-content: flex-end justify-content: flex-end
+33 -14
View File
@@ -12,7 +12,7 @@ import LoginModal from "../Modals/LoginModal";
type Props = { type Props = {
setModal: SetModal setModal: SetModal
logout: () => void logout: LogoutUser
currentUser: UserType currentUser: UserType
}; };
@@ -63,6 +63,37 @@ class Navbar extends React.Component<Props, State> {
</div> </div>
); );
const user = isMinimized ? (
<div className={'Navbar__Dropdown'}>
<div className="Navbar__Dropdown_Button Navbar__User_Profile">
<div className={'Navbar__User_Avatar'} style={{ backgroundImage: `url(${currentUser.avatar}?s=40&t=${new Date().getTime()})` }}/>
<div>{this.props.currentUser.username}</div>
</div>
<div className="Navbar__Dropdown_Menu">
<Link to={`/user/${currentUser.id}`} className="Navbar__Dropdown_Item">Профиль</Link>
{currentUser.group && hasAccessToAdminPanel(currentUser) && (
<Link to={'/kawaii__neko'} className={'Navbar__Dropdown_Item'}>
Админ-панель
</Link>
)}
<Link className="Navbar__Dropdown_Item" to={`/user/${currentUser.id}/settings`}>Настройки</Link>
<div className="Navbar__Dropdown_Item" onClick={this.logout}>Выход</div>
</div>
</div>
) : (<>
<Link to={`/user/${currentUser.id}`} className="Navbar__User_Profile">
<div className={'Navbar__User_Avatar'} style={{ backgroundImage: `url(${currentUser.avatar}?s=40&t=${new Date().getTime()})` }}/>
<div>{this.props.currentUser.username}</div>
</Link>
{currentUser.group && hasAccessToAdminPanel(currentUser) && (
<Link to={'/kawaii__neko'} className={'Navbar_Button'}>
Админ-панель
</Link>
)}
<Link className="Navbar_Button" to={`/user/${currentUser.id}/settings`}>Настройки</Link>
<div className={'Navbar_Button'} onClick={this.logout}>Выйти</div>
</>)
return ( return (
<nav className={`Navbar ${expand ? 'Expanded' : 'Minimized'}`}> <nav className={`Navbar ${expand ? 'Expanded' : 'Minimized'}`}>
{expand || !isMinimized ? links : ( {expand || !isMinimized ? links : (
@@ -74,19 +105,7 @@ class Navbar extends React.Component<Props, State> {
)} )}
{currentUser && {currentUser &&
(<div className={'Navbar__User'}> (<div className={'Navbar__User'}>
{currentUser.authorized ? (<> {currentUser.authorized ? user : (<>
<Link to={`/user/${currentUser.id}`} className="Navbar__User_Profile">
<div className={'Navbar__User_Avatar'} style={{ backgroundImage: `url(${currentUser.avatar}?s=40&t=${new Date().getTime()})` }}/>
<div>{this.props.currentUser.username}</div>
</Link>
{currentUser.group && hasAccessToAdminPanel(currentUser) && (
<Link to={'/kawaii__neko'} className={'Navbar_Button'}>
Админ-панель
</Link>
)}
<Link className="Navbar_Button" to={`/user/${currentUser.id}/settings`}>Настройки</Link>
<div className={'Navbar_Button'} onClick={this.logout}>Выйти</div>
</>) : (<>
<div className={'Navbar_Button'} onClick={() => setModal(<RegisterModal/>)}>Регистрация</div> <div className={'Navbar_Button'} onClick={() => setModal(<RegisterModal/>)}>Регистрация</div>
<div className={'Navbar_Button'} onClick={() => setModal(<LoginModal/>)}>Войти</div> <div className={'Navbar_Button'} onClick={() => setModal(<LoginModal/>)}>Войти</div>
</>)} </>)}
+41 -11
View File
@@ -13,11 +13,15 @@ import {
} from "../../api/novels"; } from "../../api/novels";
import {Link} from "react-router-dom"; import {Link} from "react-router-dom";
import Loading from "../../ui/Loading"; import Loading from "../../ui/Loading";
import { TranslateStatus } from "../../api/api"; import {TranslateExitStatus, TranslatePlatform, TranslateStatus} from "../../api/api";
import Comments from "../Comments/Comments"; import Comments from "../Comments/Comments";
import {hideModal, setModal} from "../../store/actions";
import LoadingModal from "../Modals/LoadingModal";
type Props = { type Props = {
currentUser: UserType currentUser: UserType
setModal: SetModal
hideModal: HideModal
match: { params: { novelId: number } } match: { params: { novelId: number } }
}; };
@@ -43,25 +47,30 @@ class Novel extends React.Component<Props, State> {
}; };
updateNovelStatus = (status: string) => { updateNovelStatus = (status: string) => {
const { match: { params } , currentUser } = this.props; const { match: { params } , currentUser, setModal, hideModal } = this.props;
const { userData } = this.state; const { userData } = this.state;
setModal(<LoadingModal/>);
if(userData) { if(userData) {
updateUserNovel(currentUser.id, params.novelId, { status }).then(r => { updateUserNovel(currentUser.id, params.novelId, { status }).then(r => {
this.setState({ userData: r, statusExpanded: false }) this.setState({ userData: r, statusExpanded: false });
hideModal();
}).catch(console.error); }).catch(console.error);
} else { } else {
createUserNovel(currentUser.id, params.novelId, status).then(r => { createUserNovel(currentUser.id, params.novelId, status).then(r => {
this.setState({ userData: r, statusExpanded: false }) this.setState({ userData: r, statusExpanded: false });
hideModal();
}).catch(console.error); }).catch(console.error);
} }
}; };
deleteNovelStatus = () => { deleteNovelStatus = () => {
const { match: { params } , currentUser } = this.props; const { match: { params } , currentUser, setModal, hideModal } = this.props;
deleteUserNovel(currentUser.id, params.novelId).then(() => { setModal(<LoadingModal/>);
deleteUserNovel(currentUser.id, params.novelId).then(() => {
this.setState({ userData: null, statusExpanded: false }); this.setState({ userData: null, statusExpanded: false });
hideModal();
}); });
}; };
@@ -113,7 +122,7 @@ class Novel extends React.Component<Props, State> {
}).catch((err: ApiError) => { }).catch((err: ApiError) => {
this.setState({ errorCode: err.code }); this.setState({ errorCode: err.code });
}); });
if(currentUser.authorized) if(currentUser.authorized)
fetchUserNovel(currentUser.id, params.novelId).then((userData: UserNovelType) => { fetchUserNovel(currentUser.id, params.novelId).then((userData: UserNovelType) => {
this.setState({ userData }); this.setState({ userData });
@@ -123,15 +132,26 @@ class Novel extends React.Component<Props, State> {
} }
updateNovelMark = (mark: number) => { updateNovelMark = (mark: number) => {
const { match: { params }, currentUser } = this.props; const { match: { params }, currentUser, hideModal, setModal } = this.props;
const { userData } = this.state; const { userData } = this.state;
if(userData?.mark === mark) return; if(userData?.mark === mark) return;
setModal(<LoadingModal/>);
updateUserNovel(currentUser.id, params.novelId, { mark }).then(res => { updateUserNovel(currentUser.id, params.novelId, { mark }).then(res => {
userData!!.mark = mark; userData!!.mark = mark;
this.setState({ userData }) this.setState({ userData: res });
hideModal();
}); });
} }
countNovelDuration = (t: number) => {
const h = Math.round(t / 60);
const m = t - (t*60);
if(h === 0) return `${m} мин`;
else return `${h} ч`;
}
render() { render() {
const { errorCode, novel, comments, characters, genres, userData, statusExpanded, hasMoreComments } = this.state; const { errorCode, novel, comments, characters, genres, userData, statusExpanded, hasMoreComments } = this.state;
const { currentUser } = this.props; const { currentUser } = this.props;
@@ -193,6 +213,12 @@ class Novel extends React.Component<Props, State> {
<div> <div>
<strong>Год выхода</strong>: {novel.release_date.toLocaleDateString()} <strong>Год выхода</strong>: {novel.release_date.toLocaleDateString()}
</div> </div>
<div>
<strong>Статус</strong>: {TranslateExitStatus[novel.exit_status]}
</div>
<div>
<strong>Продолжительность</strong>: {this.countNovelDuration(novel.duration)}
</div>
<div className="Novel__Info_Rating"><strong>Ср. оценка</strong>: {novel.rating.toFixed(2)}</div> <div className="Novel__Info_Rating"><strong>Ср. оценка</strong>: {novel.rating.toFixed(2)}</div>
<pre className={'Novel__Info_Description'}>{novel.description}</pre> <pre className={'Novel__Info_Description'}>{novel.description}</pre>
</div> </div>
@@ -215,7 +241,11 @@ class Novel extends React.Component<Props, State> {
} }
export default connect( export default connect(
(state: any) => ({ (state: StoreState) => ({
currentUser: state.currentUser currentUser: state.currentUser
}),
dispatch => ({
setModal: (modal: React.ReactNode | null) => dispatch(setModal(modal)),
hideModal: () => dispatch(hideModal())
}) })
)(Novel); )(Novel);
+1 -1
View File
@@ -18,4 +18,4 @@ export default (state=initialState, action: Action) => {
} }
default: return state; default: return state;
} }
} }
+7 -28
View File
@@ -1,29 +1,8 @@
type ApiRequest = (url: string, method: string, body?: string | FormData) => Promise<any>; type VersionResponse = {
version: string
}
type CommentsResponse = {
type Get = (url: string, data?: object) => Promise<any>; comments: CommentType[]
type Post = (url: string, data?: object | FormData) => Promise<any>; count: number
type Put = (url: string, data?: object) => Promise<any>; }
type Delete = (url: string, data?: object) => Promise<any>;
type FetchUsers = () => Promise<UserType[]>;
type FetchUser = (id: number) => Promise<UserType>;
type UpdateUser = (user: UserType) => Promise<UserType>;
type FetchCurrentUser = () => Promise<UserType>;
type FetchUserNovels = (id: number) => Promise<NovelType[]>;
type ActivateAccount = (token: string) => Promise<string>;
type GenerateActivateLink = () => Promise<string>;
type FetchUserSettings = () => Promise<UserSettingsType>;
type UpdateUserGeneralSettings = (use_gravatar: boolean, avatar: string) => Promise<UserType>;
type UpdateUserAppearanceSettings = (theme: string) => Promise<UserType>;
type FetchChar = (id: number) => Promise<CharacterType>;
type FetchCharNovels = (id: number) => Promise<NovelType[]>;
type Login = (login: string, password: string, recaptcha: string) => Promise<UserType>;
type Register = (login: string, password: string, email: string, recaptcha: string) => Promise<UserType>;
type Restore = (token: string, new_password: string) => Promise<string>;
type Logout = () => Promise<string>;
+7 -4
View File
@@ -1,16 +1,16 @@
type Theme = 'dark' | 'blue' | 'red' | 'green' | 'light' type Theme = 'dark' | 'blue' | 'red' | 'green' | 'light'
type ExitStatus = 'came_out'
type NovelPlatform = 'win'
type ApiError = { type ApiError = {
code: number code: number
text: string text: string
} }
type DataType = NovelType | NovelType[] | CharacterType | CharacterType[] | UserType | UserType[] | UserSettingsType | UserNovelType | UserNovelType[] | UserGroupType | CommentType | CommentType[] | GenreType | GenreType[] | PostType | PostType[]; type ApiResponse<T> = {
type ApiResponse = {
error: boolean error: boolean
error_data?: ApiError | string error_data?: ApiError | string
data?: DataType data?: T
} }
type NovelType = { type NovelType = {
@@ -22,6 +22,9 @@ type NovelType = {
status: string status: string
rating: number rating: number
release_date: Date release_date: Date
exit_status: ExitStatus
duration: number
platform: NovelPlatform
} }
type CharacterType = { type CharacterType = {
+2 -2
View File
@@ -4,8 +4,8 @@ import Loader from 'react-loader-spinner'
const Loading: React.FC = () => ( const Loading: React.FC = () => (
<div style={{ textAlign: 'center' }}> <div style={{ textAlign: 'center' }}>
<Loader color='#aaa' type='Rings' style={{ height: '100%' }} /> <Loader color='#aaa' type='Rings'/>
</div> </div>
); );
export default Loading; export default Loading;
+2 -2
View File
@@ -5,7 +5,7 @@ import {setModal} from "../../store/actions";
type Props = { type Props = {
setModal: (modal: React.ReactNode | null) => void setModal: (modal: React.ReactNode | null) => void
children: any children: React.ReactChild | React.ReactChild[]
className?: string className?: string
title?: string title?: string
} }
@@ -63,4 +63,4 @@ export default connect(null,
dispatch(setModal(modal)) dispatch(setModal(modal))
} }
}) })
)(Modal); )(Modal);
+14
View File
@@ -0,0 +1,14 @@
import React from "react";
import './Modal.sass';
type Props = {
children: React.ReactChild | React.ReactChild[]
}
const Popout = ({ children }: Props) => (
<div className="Modal__Overlay">
{children}
</div>
);
export default Popout;