изменений опять просто ёбнешься. когда я успел?
This commit is contained in:
@@ -1 +1,2 @@
|
|||||||
GENERATE_SOURCEMAP=false
|
GENERATE_SOURCEMAP=false
|
||||||
|
CDN_URL=https://cdn.unmei.space/
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
const baseUrl = process.env.NODE_ENV === "development" ? 'http://localhost:8080/v1' : 'https://api.unmei.space/v1';
|
const baseUrl = process.env.NODE_ENV === "development" ? 'http://192.168.0.103:8080/v1' : 'https://api.unmei.space/v1';
|
||||||
|
|
||||||
const request: ApiRequest = async (url: string, method: string, body?: string | FormData) => {
|
const request: ApiRequest = async (url: string, method: string, body?: string | FormData) => {
|
||||||
let bUrl = baseUrl;
|
let bUrl = baseUrl;
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class App extends React.Component<Props, State> {
|
|||||||
if(cachedTheme)
|
if(cachedTheme)
|
||||||
document.body.setAttribute('theme', cachedTheme);
|
document.body.setAttribute('theme', cachedTheme);
|
||||||
|
|
||||||
const { setUser } = this.props;
|
const { setUser } = props;
|
||||||
fetchCurrentUser().then((user: UserType) => {
|
fetchCurrentUser().then((user: UserType) => {
|
||||||
setUser(user);
|
setUser(user);
|
||||||
fetchUserSettings().then((settings: UserSettingsType) => {
|
fetchUserSettings().then((settings: UserSettingsType) => {
|
||||||
@@ -55,7 +55,8 @@ class App extends React.Component<Props, State> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}).catch((err: ApiError) => {
|
}).catch((err: ApiError) => {
|
||||||
if(err.code !== 3 && err.text) console.error(err.text);
|
if(err.code !== 3 && err.text)
|
||||||
|
console.error(err.text);
|
||||||
});
|
});
|
||||||
|
|
||||||
parser.registerTag('spoiler', SpoilerTag);
|
parser.registerTag('spoiler', SpoilerTag);
|
||||||
@@ -67,6 +68,8 @@ class App extends React.Component<Props, State> {
|
|||||||
|
|
||||||
caches.keys().then(names => {
|
caches.keys().then(names => {
|
||||||
names.forEach(name => caches.delete(name).catch(console.error))
|
names.forEach(name => caches.delete(name).catch(console.error))
|
||||||
|
}).then(() => {
|
||||||
|
window.location.reload();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,29 +5,13 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|||||||
import { faDiscord, faVk, faTelegramPlane } from '@fortawesome/free-brands-svg-icons';
|
import { faDiscord, faVk, faTelegramPlane } from '@fortawesome/free-brands-svg-icons';
|
||||||
|
|
||||||
const Footer: React.FC = () => {
|
const Footer: React.FC = () => {
|
||||||
const secretChangeTheme = () => {
|
|
||||||
const theme = localStorage.getItem('theme');
|
|
||||||
if(theme == null) {
|
|
||||||
localStorage.setItem('theme', 'light');
|
|
||||||
document.body.setAttribute('theme', 'light')
|
|
||||||
} else if(theme === 'light') {
|
|
||||||
localStorage.setItem('theme', 'dark');
|
|
||||||
document.body.setAttribute('theme', 'dark')
|
|
||||||
} else if(theme === 'dark') {
|
|
||||||
localStorage.setItem('theme', 'light');
|
|
||||||
document.body.setAttribute('theme', 'light')
|
|
||||||
}
|
|
||||||
if(theme !== 'light') {
|
|
||||||
console.log('Светлая тема находится в разработке! Не все может корректно отображаться.\nК тому же, эта кнопка секретная. Ты знал, на что шел ;)');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='Footer'>
|
<div className='Footer'>
|
||||||
<footer className='Footer__Tabs'>
|
<footer className='Footer__Tabs'>
|
||||||
<div className="Footer__Tab">
|
<div className="Footer__Tab">
|
||||||
<div className="Footer__Tab_Title" onClick={secretChangeTheme}>Команда</div>
|
<div className="Footer__Tab_Title">Команда</div>
|
||||||
<Link to={'/user/1'} className={'Link'}>Nix13</Link>
|
<Link to={'/user/1'} className={'Link'}>Nix13</Link>
|
||||||
|
<Link to={'/user/6'} className={'Link'}>RonFall</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className="Footer__Tab">
|
<div className="Footer__Tab">
|
||||||
<div className="Footer__Tab_Title">Соцсети</div>
|
<div className="Footer__Tab_Title">Соцсети</div>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import NovelItem from '../../../ui/NovelItem/NovelItem';
|
|||||||
import {capitalize, generateClassName, getRandomInt} from "../../../utils";
|
import {capitalize, generateClassName, getRandomInt} from "../../../utils";
|
||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||||
import {faAlignJustify, faThLarge} from "@fortawesome/free-solid-svg-icons";
|
import {faAlignJustify, faThLarge} from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import Button from "../../../ui/Button/Button";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
match: { params: { userId: number } }
|
match: { params: { userId: number } }
|
||||||
@@ -73,7 +74,7 @@ class UserNovels extends React.Component<Props, State> {
|
|||||||
{planned.length > 0 && <div className={'UserNovels__List'}>
|
{planned.length > 0 && <div className={'UserNovels__List'}>
|
||||||
<div className="UserNovels__List_Title">
|
<div className="UserNovels__List_Title">
|
||||||
<div style={{ marginRight: '1rem' }}>Запланированные</div>
|
<div style={{ marginRight: '1rem' }}>Запланированные</div>
|
||||||
<button onClick={this.getRandomNovel}>Рандомная новелла</button>
|
<Button onClick={this.getRandomNovel}>Рандомная новелла</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className={generateClassName("UserNovels__List_Novels", capitalize(viewType) || 'Grid')}>
|
<div className={generateClassName("UserNovels__List_Novels", capitalize(viewType) || 'Grid')}>
|
||||||
{planned.map((novel, id) => <NovelItem {...novel} key={novel.id} novelId={id+1} viewType={viewType}/>)}
|
{planned.map((novel, id) => <NovelItem {...novel} key={novel.id} novelId={id+1} viewType={viewType}/>)}
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import Button from "../../../ui/Button/Button";
|
||||||
|
import Group from "../../../ui/Group/Group";
|
||||||
|
import React, {FormEvent} from "react";
|
||||||
|
import {fetchUserSettings, updateUserAppearanceSettings} from "../../../api/users";
|
||||||
|
import {setUser} from "../../../store/actions";
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
theme: Theme
|
||||||
|
}
|
||||||
|
|
||||||
|
class Appearance extends React.Component<{}, State> {
|
||||||
|
state: State = {
|
||||||
|
theme: 'dark'
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
fetchUserSettings().then(settings => {
|
||||||
|
this.setState({ theme: settings.theme })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
saveTheme = (event: FormEvent) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const { theme } = this.state;
|
||||||
|
updateUserAppearanceSettings(theme).then(user => {
|
||||||
|
setUser(user);
|
||||||
|
|
||||||
|
localStorage.setItem('theme', theme);
|
||||||
|
document.body.setAttribute('theme', theme);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { theme } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group title={'Тема'} className={'Settings_Group'}>
|
||||||
|
<form onSubmit={this.saveTheme}>
|
||||||
|
<select onChange={e => this.setState({ theme: e.target.value as Theme })} value={theme}>
|
||||||
|
<option value="dark">Dark</option>
|
||||||
|
<option value="blue">Blue</option>
|
||||||
|
<option value="red">Red</option>
|
||||||
|
<option value="green">Green</option>
|
||||||
|
<option value="light">Light</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<Button>Сохранить</Button>
|
||||||
|
</form>
|
||||||
|
</Group>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Appearance;
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import React, {ChangeEvent, FormEvent} from "react";
|
||||||
|
import Button from "../../../ui/Button/Button";
|
||||||
|
import Group from "../../../ui/Group/Group";
|
||||||
|
import {fetchUserSettings, updateUserGeneralSettings, uploadAvatar} from "../../../api/users";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
setUser: (user: UserType) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
// Avatar
|
||||||
|
useGravatar: boolean
|
||||||
|
avatar: File | null
|
||||||
|
}
|
||||||
|
|
||||||
|
class General extends React.Component<Props, State> {
|
||||||
|
state: State = {
|
||||||
|
useGravatar: false, avatar: null
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
fetchUserSettings().then(settings => {
|
||||||
|
this.setState({ useGravatar: settings.use_gravatar })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeAvatar = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const files = event.target.files;
|
||||||
|
if(!files) return;
|
||||||
|
this.setState({ avatar: files[0] });
|
||||||
|
}
|
||||||
|
|
||||||
|
saveAvatar = (event: FormEvent) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const { avatar, useGravatar} = this.state;
|
||||||
|
const { setUser } = this.props;
|
||||||
|
|
||||||
|
if(avatar) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('avatar', avatar);
|
||||||
|
|
||||||
|
uploadAvatar(formData).then(avatarUrl => {
|
||||||
|
updateUserGeneralSettings(useGravatar, avatarUrl).then(setUser)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
updateUserGeneralSettings(useGravatar, '').then(setUser)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { useGravatar } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group title={'Аватар'} className={'Settings_Group'}>
|
||||||
|
<form onSubmit={this.saveAvatar}>
|
||||||
|
<label>Использовать Gravatar <input type="checkbox" onChange={event => this.setState({ useGravatar: event.target.checked })} checked={useGravatar}/></label>
|
||||||
|
<label>Загрузить аватар <input type="file" onChange={this.onChangeAvatar} disabled={useGravatar}/></label>
|
||||||
|
<Button>Сохранить</Button>
|
||||||
|
</form>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default General;
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import React from "react";
|
||||||
|
import Group from "../../../ui/Group/Group";
|
||||||
|
import Input from "../../../ui/Input/Input";
|
||||||
|
import Button from "../../../ui/Button/Button";
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
oldEmail: string
|
||||||
|
email1: string
|
||||||
|
email2: string
|
||||||
|
error: string
|
||||||
|
}
|
||||||
|
|
||||||
|
class Security extends React.Component<{}, State> {
|
||||||
|
state: State = {
|
||||||
|
oldEmail: '', email1: '', email2: '',
|
||||||
|
error: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
changeEmail = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const { oldEmail, email1, email2 } = this.state;
|
||||||
|
let error = '';
|
||||||
|
if(!oldEmail) error += 'Введите старый E-mail\n';
|
||||||
|
if(!email1) error += 'Введите новый E-mail\n';
|
||||||
|
if(!email2) error += 'Введите повтор нового E-mail\n';
|
||||||
|
if(email1 !== email2) error += 'E-mail не совпадают\n';
|
||||||
|
if(oldEmail === email1) error += 'Старый и новый E-mail не должны совпадать';
|
||||||
|
|
||||||
|
if(error) {
|
||||||
|
this.setState({ error });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { oldEmail, email1, email2, error } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group title={'Смена E-mail'} className={'Settings_Group'}>
|
||||||
|
{error && <pre className={'Error'}>{error}</pre>}
|
||||||
|
<form onSubmit={this.changeEmail}>
|
||||||
|
<Input
|
||||||
|
placeholder={'Старый E-mail'}
|
||||||
|
type={'email'}
|
||||||
|
value={oldEmail}
|
||||||
|
onChange={e => this.setState({ oldEmail: e.target.value })}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
placeholder={'Новый E-mail'}
|
||||||
|
type={'email'}
|
||||||
|
value={email1}
|
||||||
|
onChange={e => this.setState({ email1: e.target.value })}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
placeholder={'Новый E-mail ещё раз'}
|
||||||
|
type={'email'}
|
||||||
|
value={email2}
|
||||||
|
onChange={e => this.setState({ email1: e.target.value })}
|
||||||
|
/>
|
||||||
|
<Button>Сохранить</Button>
|
||||||
|
</form>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Security;
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
.Group
|
.Group
|
||||||
.Settings_Avatar
|
.Settings_Group
|
||||||
|
font-size: 1.1rem
|
||||||
|
|
||||||
form
|
form
|
||||||
display: flex
|
display: flex
|
||||||
flex-direction: column
|
flex-direction: column
|
||||||
@@ -8,5 +10,5 @@
|
|||||||
input
|
input
|
||||||
margin: .5rem 0
|
margin: .5rem 0
|
||||||
|
|
||||||
.Button
|
input, .Button
|
||||||
flex-grow: 0
|
font-size: 1.1rem
|
||||||
|
|||||||
@@ -1,21 +1,18 @@
|
|||||||
import React, {ChangeEvent, FormEvent} from "react";
|
import React from "react";
|
||||||
import Group from "../../../ui/Group/Group";
|
import Group from "../../../ui/Group/Group";
|
||||||
import {connect} from "react-redux";
|
import {connect} from "react-redux";
|
||||||
import NotFoundError from "../../NotFoundError";
|
import NotFoundError from "../../NotFoundError";
|
||||||
import Button from "../../../ui/Button/Button";
|
|
||||||
import './Settings.sass';
|
import './Settings.sass';
|
||||||
import {
|
|
||||||
fetchUserSettings,
|
|
||||||
updateUserAppearanceSettings,
|
|
||||||
updateUserGeneralSettings,
|
|
||||||
uploadAvatar
|
|
||||||
} from "../../../api/users";
|
|
||||||
import {setUser} from "../../../store/actions";
|
import {setUser} from "../../../store/actions";
|
||||||
import Tabs from "../../../ui/Tabs/Tabs";
|
import Tabs from "../../../ui/Tabs/Tabs";
|
||||||
import TabItem from "../../../ui/Tabs/TabItem";
|
import TabItem from "../../../ui/Tabs/TabItem";
|
||||||
|
import Appearance from "./Appearance";
|
||||||
|
import General from "./General";
|
||||||
|
import Security from "./Security";
|
||||||
|
|
||||||
enum TabsNames {
|
enum TabsNames {
|
||||||
General,
|
General,
|
||||||
|
Security,
|
||||||
Appearance
|
Appearance
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,109 +23,53 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
// Avatar
|
|
||||||
useGravatar: boolean
|
|
||||||
avatar: File | null
|
|
||||||
theme: Theme
|
|
||||||
|
|
||||||
currentTab: TabsNames
|
currentTab: TabsNames
|
||||||
}
|
}
|
||||||
|
|
||||||
class Settings extends React.Component<Props, State> {
|
class Settings extends React.Component<Props, State> {
|
||||||
state: State = {
|
state: State = {
|
||||||
useGravatar: false, avatar: null, theme: "dark",
|
|
||||||
currentTab: TabsNames.General
|
currentTab: TabsNames.General
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
fetchUserSettings().then(settings => {
|
|
||||||
this.setState({ useGravatar: settings.use_gravatar })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
onChangeAvatar = (event: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const files = event.target.files;
|
|
||||||
if(!files) return;
|
|
||||||
this.setState({ avatar: files[0] });
|
|
||||||
}
|
|
||||||
|
|
||||||
saveAvatar = (event: FormEvent) => {
|
|
||||||
event.preventDefault();
|
|
||||||
const { avatar, useGravatar} = this.state;
|
|
||||||
const { setUser } = this.props;
|
|
||||||
|
|
||||||
if(avatar) {
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('avatar', avatar);
|
|
||||||
|
|
||||||
uploadAvatar(formData).then(avatarUrl => {
|
|
||||||
updateUserGeneralSettings(useGravatar, avatarUrl).then(setUser)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
updateUserGeneralSettings(useGravatar, '').then(setUser)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
saveTheme = (event: FormEvent) => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const { theme } = this.state;
|
|
||||||
updateUserAppearanceSettings(theme).then(user => {
|
|
||||||
setUser(user);
|
|
||||||
|
|
||||||
localStorage.setItem('theme', theme);
|
|
||||||
document.body.setAttribute('theme', theme);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { currentUser, match: { params: { userId } } } = this.props;
|
const { currentUser, match: { params: { userId } } } = this.props;
|
||||||
const { useGravatar, currentTab, theme } = this.state
|
const { currentTab } = this.state
|
||||||
|
|
||||||
if(parseInt(userId) !== currentUser.id) return <NotFoundError/>;
|
if(parseInt(userId) !== currentUser.id) return <NotFoundError/>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group title={'Настройки'}>
|
<Group title={''}>
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<TabItem
|
<TabItem
|
||||||
selected={currentTab === TabsNames.General}
|
selected={currentTab === TabsNames.General}
|
||||||
onClick={() => this.setState({ currentTab: TabsNames.General })}
|
onClick={() => this.setState({ currentTab: TabsNames.General })}
|
||||||
>
|
>
|
||||||
Главное
|
Главное
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem
|
<TabItem
|
||||||
selected={currentTab === TabsNames.Appearance}
|
selected={currentTab === TabsNames.Security}
|
||||||
onClick={() => this.setState({ currentTab: TabsNames.Appearance })}
|
onClick={() => this.setState({ currentTab: TabsNames.Security })}
|
||||||
>
|
>
|
||||||
Внешний вид
|
Безопастность
|
||||||
</TabItem>
|
</TabItem>
|
||||||
</Tabs>
|
<TabItem
|
||||||
{currentTab === TabsNames.General && (
|
selected={currentTab === TabsNames.Appearance}
|
||||||
<Group title={'Аватар'} className={'Settings_Avatar'}>
|
onClick={() => this.setState({ currentTab: TabsNames.Appearance })}
|
||||||
<form onSubmit={this.saveAvatar}>
|
>
|
||||||
<label>Использовать Gravatar <input type="checkbox" onChange={event => this.setState({ useGravatar: event.target.checked })} checked={useGravatar}/></label>
|
Внешний вид
|
||||||
<label>Загрузить аватар <input type="file" onChange={this.onChangeAvatar} disabled={useGravatar}/></label>
|
</TabItem>
|
||||||
<Button>Сохранить</Button>
|
</Tabs>
|
||||||
</form>
|
{currentTab === TabsNames.General && (
|
||||||
</Group>
|
<General setUser={setUser}/>
|
||||||
)}
|
)}
|
||||||
{currentTab === TabsNames.Appearance && (
|
{currentTab === TabsNames.Security && (
|
||||||
<Group title={'Тема'} className={'Settings_Avatar'}>
|
<Security/>
|
||||||
<form onSubmit={this.saveTheme}>
|
)}
|
||||||
<select onChange={e => this.setState({ theme: e.target.value as Theme })} value={theme}>
|
{currentTab === TabsNames.Appearance && (
|
||||||
<option value="dark">Dark</option>
|
<Appearance/>
|
||||||
<option value="blue">Blue</option>
|
)}
|
||||||
<option value="red">Red</option>
|
</Group>
|
||||||
<option value="green">Green</option>
|
);
|
||||||
<option value="light">Light</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<Button>Сохранить</Button>
|
|
||||||
</form>
|
|
||||||
</Group>
|
|
||||||
)}
|
|
||||||
</Group>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+22
-8
@@ -27,19 +27,32 @@ class User extends React.Component<Props, State> {
|
|||||||
user: null, novels: [], errorCode: null
|
user: null, novels: [], errorCode: null
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
getAndSetUser = (id: number) => {
|
||||||
const params = this.props.match.params;
|
fetchUser(id).then(user => {
|
||||||
|
|
||||||
fetchUser(parseInt(params.userId)).then((user: UserType) => {
|
|
||||||
this.setState({ user });
|
this.setState({ user });
|
||||||
document.title = `${user.username} / Unmei`;
|
document.title = `${user.username} / Unmei`;
|
||||||
|
|
||||||
fetchUserNovels(parseInt(params.userId)).then((novels: NovelType[]) => {
|
fetchUserNovels(id).then(novels => {
|
||||||
this.setState({ novels });
|
this.setState({ novels });
|
||||||
}).catch((err: ApiError) => console.error(err));
|
}).catch((err: ApiError) => console.error(err));
|
||||||
}).catch((err: ApiError) => this.setState({ errorCode: err.code }));
|
}).catch((err: ApiError) => this.setState({ errorCode: err.code }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps: Readonly<Props>, prevState: Readonly<State>, _?: any) {
|
||||||
|
const newUserId = parseInt(this.props.match.params.userId);
|
||||||
|
const prevUserId = parseInt(prevProps.match.params.userId);
|
||||||
|
|
||||||
|
if(newUserId !== prevUserId) {
|
||||||
|
this.getAndSetUser(newUserId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const params = this.props.match.params;
|
||||||
|
|
||||||
|
this.getAndSetUser(parseInt(params.userId))
|
||||||
|
}
|
||||||
|
|
||||||
sendActivateLink = () => {
|
sendActivateLink = () => {
|
||||||
generateActivateLink().then(() => {
|
generateActivateLink().then(() => {
|
||||||
const notification = (
|
const notification = (
|
||||||
@@ -48,10 +61,11 @@ class User extends React.Component<Props, State> {
|
|||||||
</NotificationMessage>
|
</NotificationMessage>
|
||||||
);
|
);
|
||||||
this.props.addNotification(notification);
|
this.props.addNotification(notification);
|
||||||
}).catch(err => {
|
}).catch((err: ApiError) => {
|
||||||
const notification = (
|
const notification = (
|
||||||
<NotificationMessage level={"success"}>
|
<NotificationMessage level={"error"}>
|
||||||
Сообщение успешно отправлено!
|
Произошла ошибка! №{err.code} <br/>
|
||||||
|
{err.text}
|
||||||
</NotificationMessage>
|
</NotificationMessage>
|
||||||
);
|
);
|
||||||
this.props.addNotification(notification);
|
this.props.addNotification(notification);
|
||||||
|
|||||||
@@ -9,3 +9,6 @@
|
|||||||
font-size: 1.2rem
|
font-size: 1.2rem
|
||||||
font-weight: 600
|
font-weight: 600
|
||||||
margin-bottom: .5rem
|
margin-bottom: .5rem
|
||||||
|
|
||||||
|
&__Content
|
||||||
|
margin-left: .5rem
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const Group = (restProps: Props) => {
|
|||||||
return (
|
return (
|
||||||
<div className={classNames('Group', className)} {...props}>
|
<div className={classNames('Group', className)} {...props}>
|
||||||
<div className="Group__Title">{title}</div>
|
<div className="Group__Title">{title}</div>
|
||||||
<div style={style}>
|
<div style={style} className={'Group__Content'}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, {InputHTMLAttributes} from "react";
|
|||||||
import classNames from "../../classNames";
|
import classNames from "../../classNames";
|
||||||
import './Input.sass';
|
import './Input.sass';
|
||||||
|
|
||||||
const Input = (restProps: InputHTMLAttributes<HTMLElement>) => {
|
const Input = (restProps: InputHTMLAttributes<HTMLInputElement>) => {
|
||||||
const { className, ...props } = restProps;
|
const { className, ...props } = restProps;
|
||||||
return (
|
return (
|
||||||
<input className={classNames('Input', className)} {...props} />
|
<input className={classNames('Input', className)} {...props} />
|
||||||
|
|||||||
+9
-1
@@ -20,4 +20,12 @@ const generateClassName = (baseClassName: string, ...classNames: string[]) => {
|
|||||||
else return `${baseClassName} ${classNames.join(" ")}`
|
else return `${baseClassName} ${classNames.join(" ")}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export { capitalize, getRandomInt, hasPermission, hasAccessToAdminPanel, generateClassName };
|
const getImage: (path: string) => string = (path: string) => {
|
||||||
|
let cdnUrl = process.env['CDN_URL'];
|
||||||
|
if(!cdnUrl) return path;
|
||||||
|
if(cdnUrl.endsWith('/')) cdnUrl = cdnUrl.slice(cdnUrl.length-1);
|
||||||
|
if(path.startsWith('/')) path = path.slice(1, path.length);
|
||||||
|
return `${cdnUrl}/${path}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { capitalize, getRandomInt, hasPermission, hasAccessToAdminPanel, generateClassName, getImage };
|
||||||
|
|||||||
Reference in New Issue
Block a user