finished types; novel improvement; fixed navbar for mobile
This commit is contained in:
Generated
+9
-9
@@ -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
@@ -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
@@ -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');
|
||||||
|
|||||||
@@ -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`);
|
||||||
|
|||||||
+3
-3
@@ -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}`);
|
||||||
+11
-11
@@ -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
@@ -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 });
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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
@@ -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>
|
||||||
</>)}
|
</>)}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
setModal(<LoadingModal/>);
|
||||||
deleteUserNovel(currentUser.id, params.novelId).then(() => {
|
deleteUserNovel(currentUser.id, params.novelId).then(() => {
|
||||||
this.setState({ userData: null, statusExpanded: false });
|
this.setState({ userData: null, statusExpanded: false });
|
||||||
|
hideModal();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -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);
|
||||||
Vendored
+7
-28
@@ -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>;
|
|
||||||
|
|||||||
Vendored
+7
-4
@@ -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 = {
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ 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>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
Reference in New Issue
Block a user