v1.1; some profile beauty; snowfall
This commit is contained in:
+4
-3
@@ -6,8 +6,8 @@
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.32",
|
||||
"@fortawesome/free-brands-svg-icons": "5.15.1",
|
||||
"@fortawesome/free-solid-svg-icons": "5.15.1",
|
||||
"@fortawesome/react-fontawesome": "0.1.12",
|
||||
"@sentry/react": "5.27.4",
|
||||
"@fortawesome/react-fontawesome": "0.1.13",
|
||||
"@sentry/react": "5.28.0",
|
||||
"bbcode-to-react": "0.2.9",
|
||||
"eruda": "2.4.1",
|
||||
"eruda-dom": "2.0.0",
|
||||
@@ -18,6 +18,7 @@
|
||||
"react-loader-spinner": "3.1.14",
|
||||
"react-redux": "7.2.2",
|
||||
"react-router-dom": "5.2.0",
|
||||
"react-snowfall": "^1.0.2",
|
||||
"redux": "4.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -27,9 +28,9 @@
|
||||
"@types/react-redux": "7.1.11",
|
||||
"@types/react-router-dom": "5.1.6",
|
||||
"@types/react-transition-group": "4.4.0",
|
||||
"sass": "1.29.0",
|
||||
"react-scripts": "3.4.4",
|
||||
"redux-devtools-extension": "2.13.8",
|
||||
"sass": "1.29.0",
|
||||
"typescript": "4.1.2"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
+12
-15
@@ -2,12 +2,17 @@ const baseUrl = process.env.NODE_ENV === "development" ? 'http://localhost:8080/
|
||||
|
||||
async function request<T>(url: string, method: string, body?: string | FormData): Promise<T> {
|
||||
let bUrl = baseUrl;
|
||||
if(baseUrl.endsWith('/')) bUrl = bUrl.slice(0, baseUrl.length - 1);
|
||||
if(url.startsWith('/')) url = url.slice(1, baseUrl.length);
|
||||
if(baseUrl.endsWith('/'))
|
||||
bUrl = bUrl.slice(0, baseUrl.length - 1);
|
||||
if(url.startsWith('/'))
|
||||
url = url.slice(1, baseUrl.length);
|
||||
|
||||
const res = await fetch(`${bUrl}/${url}`, {
|
||||
method, body,
|
||||
credentials: "include"
|
||||
credentials: "include",
|
||||
headers: new Headers({
|
||||
'Dev': /dev.(.+)/.test(window.location.href) ? '1' : '0'
|
||||
})
|
||||
});
|
||||
|
||||
let json: ApiResponse<T>;
|
||||
@@ -26,18 +31,10 @@ async function request<T>(url: string, method: string, body?: string | FormData)
|
||||
return Promise.resolve(json.data);
|
||||
}
|
||||
|
||||
function get<T>(url: string, data?: object): Promise<T> {
|
||||
return request<T>(url, 'GET', 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 get = <T>(url: string, data?: object): Promise<T> => request<T>(url, 'GET', JSON.stringify(data));
|
||||
const post = <T>(url: string, data?: object | FormData): Promise<T> => request(url, 'POST', data instanceof FormData ? data : JSON.stringify(data));
|
||||
const put = <T>(url: string, data?: object): Promise<T> => request<T>(url, 'PUT', JSON.stringify(data));
|
||||
const del = <T>(url: string, data?: object): Promise<T> => request<T>(url, 'DELETE', JSON.stringify(data));
|
||||
|
||||
const TranslateStatus: { [id: string]: string } = {
|
||||
planned: 'Запланировано',
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import { get } from "./api";
|
||||
|
||||
export const getClub = (id: number) => get<ClubType>(`/clubs/${id}`);
|
||||
+13
-2
@@ -65,8 +65,17 @@ body[theme='light']
|
||||
--block-sec-color: #{$blockSecondaryBackgroundColorLight}
|
||||
--nav-bg-color: #{$navbarBackgroundColorLight}
|
||||
|
||||
color: var(--main-color)
|
||||
background: var(--main-bg-color)
|
||||
body[theme='win95']
|
||||
--main-color: #{$mainColorLight}
|
||||
--link-color: #{$linkHoverColorLight}
|
||||
--sec-color: #{$secondaryColorLight}
|
||||
|
||||
--main-bg-color: #008083
|
||||
--sec-bg-color: #c0c0c0
|
||||
--nav-bg-color: #c0c0c0
|
||||
|
||||
.Navbar
|
||||
box-shadow: 0 1px 1px 1px white
|
||||
|
||||
a
|
||||
color: var(--main-color)
|
||||
@@ -96,7 +105,9 @@ a
|
||||
margin-bottom: 1rem
|
||||
|
||||
.Error
|
||||
color: #{mainColor}
|
||||
background-color: var(--error-bg-color)
|
||||
|
||||
.Successful
|
||||
color: #{mainColor}
|
||||
background-color: var(--success-bg-color)
|
||||
|
||||
+33
-7
@@ -9,8 +9,6 @@ import NotFoundError from "../NotFoundError";
|
||||
import { fetchCurrentUser, fetchUserSettings } from "../../api/users";
|
||||
import { connect } from "react-redux";
|
||||
import Character from "../Character/Character";
|
||||
// @ts-ignore
|
||||
import parser from 'bbcode-to-react';
|
||||
import SpoilerTag from "../../ui/Spoiler/SpoilerTag";
|
||||
import Novels from "../Novels/Novels";
|
||||
import Footer from '../Footer/Footer';
|
||||
@@ -28,21 +26,30 @@ import Settings from "../User/Settings/Settings";
|
||||
import { setUser } from "../../store/ducks/currentUser";
|
||||
import { setSettings } from "../../store/ducks/userSettings";
|
||||
import { withProfiler } from "@sentry/react";
|
||||
import { hasPermission } from "../../utils";
|
||||
import Club from "../Club/Club";
|
||||
import Snowfall from "react-snowfall";
|
||||
// @ts-ignore
|
||||
import eruda from 'eruda';
|
||||
import { hasPermission } from "../../utils";
|
||||
// @ts-ignore
|
||||
import parser from 'bbcode-to-react';
|
||||
|
||||
type Props = {
|
||||
notifications: React.ReactNode[]
|
||||
modal: React.ReactNode
|
||||
setUser: SetUser
|
||||
setSettings: SetSettings
|
||||
snowfall: Snowfall
|
||||
};
|
||||
|
||||
type State = {}
|
||||
type State = {
|
||||
user: UserType | null
|
||||
};
|
||||
|
||||
class App extends React.Component<Props, State> {
|
||||
state: State = {}
|
||||
state: State = {
|
||||
user: null
|
||||
};
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
@@ -60,6 +67,9 @@ class App extends React.Component<Props, State> {
|
||||
|
||||
fetchCurrentUser().then(user => {
|
||||
setUser(user);
|
||||
this.setState({ user });
|
||||
localStorage.setItem('banned', JSON.stringify(user.is_banned));
|
||||
|
||||
fetchUserSettings().then(settings => {
|
||||
setSettings(settings);
|
||||
if(settings.theme !== cachedTheme) {
|
||||
@@ -70,6 +80,7 @@ class App extends React.Component<Props, State> {
|
||||
if(hasPermission(user, 'mobile_debug'))
|
||||
eruda.init();
|
||||
}).catch((err: ApiError) => {
|
||||
console.log(err)
|
||||
if(err.code !== 3 && err.text)
|
||||
console.error(err.text);
|
||||
});
|
||||
@@ -88,10 +99,22 @@ class App extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { modal } = this.props;
|
||||
const { modal, snowfall } = this.props;
|
||||
const { user } = this.state;
|
||||
const isBanned = user?.is_banned || JSON.parse(localStorage.getItem('banned') || 'false');
|
||||
|
||||
if(isBanned) {
|
||||
return (
|
||||
<div style={{ display: 'flex', height: '100%', justifyContent: "center", alignItems: "center", fontSize: '2rem' }}>
|
||||
Вы были забанены!
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{snowfall.snowfallStatus && (
|
||||
<Snowfall color={'white'} snowflakeCount={snowfall.snowflakeCount} style={{ zIndex: 1000 }}/>
|
||||
)}
|
||||
<Navbar/>
|
||||
<div className={'Container'}>
|
||||
<div className="Notifications">
|
||||
@@ -110,6 +133,8 @@ class App extends React.Component<Props, State> {
|
||||
<Route exact path={'/novels'} component={Novels}/>
|
||||
<Route exact path={'/novels/:novelId'} component={Novel}/>
|
||||
|
||||
<Route exact path={'/clubs/:clubId'} component={Club}/>
|
||||
|
||||
<Route exact path={'/users'} component={Users}/>
|
||||
<Route exact path={'/user/:userId'} component={User}/>
|
||||
<Route exact path={'/user/:userId/novels'} component={UserNovels}/>
|
||||
@@ -138,7 +163,8 @@ class App extends React.Component<Props, State> {
|
||||
export default connect(
|
||||
(state: StoreState) => ({
|
||||
notifications: state.notifications,
|
||||
modal: state.modal
|
||||
modal: state.modal,
|
||||
snowfall: state.snowfall
|
||||
}),
|
||||
dispatch => ({
|
||||
setUser: (userData: UserType) => dispatch(setUser(userData)),
|
||||
|
||||
@@ -15,12 +15,10 @@
|
||||
margin-top: 1rem
|
||||
|
||||
.Character__Image
|
||||
width: 150px !important
|
||||
width: 150px
|
||||
height: 250px
|
||||
margin-right: .5rem
|
||||
border-radius: $borderRadius
|
||||
background-size: cover
|
||||
background-position: center
|
||||
|
||||
.Character__Info
|
||||
display: flex
|
||||
|
||||
@@ -38,7 +38,7 @@ class Character extends React.Component<Props, State> {
|
||||
return (
|
||||
<div className={'Character'}>
|
||||
<div className="Character__Main">
|
||||
<div style={{ backgroundImage: `url(${char.image})` }} className="Character__Image"/>
|
||||
<img src={char.image} alt={char.original_name} className="Character__Image" />
|
||||
<div className="Character__Info">
|
||||
<div className="Character__Info_Name">
|
||||
{char.original_name !== char.localized_name ? (
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import React from "react";
|
||||
import { getClub } from "../../api/clubs";
|
||||
import Loading from "../../ui/Loading";
|
||||
import Group from "../../ui/Group/Group";
|
||||
|
||||
type Props = {
|
||||
match: { params: { clubId: number } }
|
||||
};
|
||||
|
||||
type State = {
|
||||
club: ClubType | null
|
||||
}
|
||||
|
||||
class Club extends React.Component<Props, State> {
|
||||
state: State = {
|
||||
club: null
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
getClub(this.props.match.params.clubId).then(club => {
|
||||
this.setState({ club });
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
if(!this.state.club) return <Loading/>;
|
||||
return (
|
||||
<div>
|
||||
<Group title={this.state.club.name}>
|
||||
|
||||
</Group>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Club;
|
||||
@@ -43,8 +43,6 @@ class LoginModal extends React.Component<Props, State> {
|
||||
const { login: log, password, recaptcha } = this.state;
|
||||
|
||||
login(log, password, recaptcha).then(user => {
|
||||
if(!user) return;
|
||||
|
||||
setUser(user);
|
||||
const successful = (
|
||||
<NotificationMessage level={"success"} position={"center"}>
|
||||
|
||||
@@ -4,19 +4,28 @@ import React, { FormEvent } from "react";
|
||||
import { updateUserAppearanceSettings } from "../../../api/users";
|
||||
import { connect } from "react-redux";
|
||||
import { setUser } from "../../../store/ducks/currentUser";
|
||||
import { setSnowflakeCount, setSnowfallStatus } from "../../../store/ducks/snowfall";
|
||||
|
||||
type Props = {
|
||||
setUser: SetUser
|
||||
settings: UserSettingsType
|
||||
|
||||
snowfallSettings: Snowfall
|
||||
setSnowflakeCount: SetSnowflakeCount
|
||||
setSnowfallStatus: SetSnowflakeStatus
|
||||
}
|
||||
|
||||
type State = {
|
||||
theme: Theme
|
||||
colorName: boolean
|
||||
snowflakeCount: 0
|
||||
}
|
||||
|
||||
class Appearance extends React.Component<Props, State> {
|
||||
state: State = {
|
||||
theme: this.props.settings.theme
|
||||
theme: this.props.settings.theme,
|
||||
colorName: JSON.parse(localStorage.getItem('color-name') || 'false'),
|
||||
snowflakeCount: JSON.parse(localStorage.getItem('snowflakeCount') ?? '250')
|
||||
}
|
||||
|
||||
saveTheme = (event: FormEvent) => {
|
||||
@@ -25,18 +34,19 @@ class Appearance extends React.Component<Props, State> {
|
||||
const { theme } = this.state;
|
||||
const { setUser } = this.props;
|
||||
|
||||
localStorage.setItem('theme', theme);
|
||||
document.body.setAttribute('theme', theme);
|
||||
|
||||
updateUserAppearanceSettings(theme).then(user => {
|
||||
setUser(user);
|
||||
|
||||
localStorage.setItem('theme', theme);
|
||||
document.body.setAttribute('theme', theme);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { theme } = this.state;
|
||||
const { snowfallSettings, setSnowflakeCount, setSnowfallStatus } = this.props;
|
||||
const { theme, colorName } = this.state;
|
||||
|
||||
return (
|
||||
return (<>
|
||||
<Group title={'Тема'} className={'Settings_Group'}>
|
||||
<form onSubmit={this.saveTheme}>
|
||||
<select onChange={e => this.setState({ theme: e.target.value as Theme })} value={theme}>
|
||||
@@ -49,15 +59,64 @@ class Appearance extends React.Component<Props, State> {
|
||||
<Button>Сохранить</Button>
|
||||
</form>
|
||||
</Group>
|
||||
)
|
||||
<Group title={'Остальное'}>
|
||||
<label>
|
||||
Выделять ник цветом группы
|
||||
<input
|
||||
type="checkbox"
|
||||
onChange={e => {
|
||||
localStorage.setItem('color-name', JSON.stringify(e.target.checked));
|
||||
this.setState({ colorName: e.target.checked });
|
||||
}}
|
||||
checked={colorName}
|
||||
/>
|
||||
</label>
|
||||
</Group>
|
||||
<Group title={'Снег'}>
|
||||
<label>
|
||||
Включить снег
|
||||
<input
|
||||
type={'checkbox'}
|
||||
checked={snowfallSettings.snowfallStatus}
|
||||
onChange={e => {
|
||||
setSnowfallStatus(e.target.checked);
|
||||
localStorage.setItem('snowfallStatus', JSON.stringify(e.target.checked));
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label style={{ display: "flex", flexDirection: "column" }}>
|
||||
<div>
|
||||
Количество снежинок {snowfallSettings.snowflakeCount}
|
||||
{snowfallSettings.snowflakeCount > 700 && (
|
||||
<span style={{ color: 'red' }}> (использование большого количества снежинок может негативно сказаться на производительности!)</span>
|
||||
)}
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
min={0}
|
||||
max={1000}
|
||||
value={snowfallSettings.snowflakeCount}
|
||||
onChange={e => {
|
||||
const value = JSON.parse(e.target.value);
|
||||
setSnowflakeCount(value);
|
||||
localStorage.setItem('snowflakeCount', value);
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
</Group>
|
||||
</>);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
(state: StoreState) => ({
|
||||
settings: state.userSettings
|
||||
settings: state.userSettings,
|
||||
snowfallSettings: state.snowfall
|
||||
}),
|
||||
dispatch => ({
|
||||
setUser: (user: UserType) => dispatch(setUser(user))
|
||||
setUser: (user: UserType) => dispatch(setUser(user)),
|
||||
setSnowflakeCount: (count: number) => dispatch(setSnowflakeCount(count)),
|
||||
setSnowfallStatus: (status: boolean) => dispatch(setSnowfallStatus(status))
|
||||
})
|
||||
)(Appearance);
|
||||
|
||||
@@ -41,16 +41,30 @@
|
||||
font-weight: 600
|
||||
|
||||
&_Group
|
||||
background-color: #0008
|
||||
border-color: #eeeeee
|
||||
border-width: 1px 1px 1px 4px
|
||||
border-width: 1px
|
||||
border-style: solid
|
||||
color: var(--main-color)
|
||||
|
||||
border-radius: 4px
|
||||
border-radius: 2px
|
||||
padding: .2rem
|
||||
text-align: center
|
||||
font-size: 1.1rem
|
||||
font-weight: 300
|
||||
position: relative
|
||||
z-index: 1
|
||||
|
||||
&::before
|
||||
content: ''
|
||||
width: 100%
|
||||
border-top-color: inherit
|
||||
border-top-width: 27px
|
||||
border-top-style: solid
|
||||
opacity: 0.5
|
||||
position: absolute
|
||||
z-index: -1
|
||||
top: 0
|
||||
left: 0
|
||||
|
||||
&_Avatar
|
||||
background-size: cover
|
||||
@@ -80,7 +94,6 @@
|
||||
@media (max-width: 460px)
|
||||
width: calc(100% - 1rem)
|
||||
|
||||
|
||||
padding: .5rem 0
|
||||
margin: 0 .5rem
|
||||
|
||||
|
||||
+20
-12
@@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import React, { CSSProperties } from "react";
|
||||
import { fetchUser, fetchUserNovels, generateActivateLink } from "../../api/users";
|
||||
import './User.sass'
|
||||
import NotFoundError from "../NotFoundError";
|
||||
@@ -78,7 +78,9 @@ class User extends React.Component<Props, State> {
|
||||
if(errorCode === 100) return <NotFoundError/>;
|
||||
if(!user) return <Loading/>;
|
||||
|
||||
const style = !user.is_activated ? { marginTop: '1rem' } : {}
|
||||
const style: CSSProperties = !user.is_activated ? { marginTop: '1rem' } : {};
|
||||
const userNameStyle: CSSProperties = JSON.parse(localStorage.getItem('color-name') || 'false') && user.group.id !== 0 ? { color: user.group.color } : {};
|
||||
console.log(userNameStyle, localStorage.getItem('color-name'));
|
||||
|
||||
return (
|
||||
<div className={'User'}>
|
||||
@@ -88,8 +90,12 @@ class User extends React.Component<Props, State> {
|
||||
<p>Активируйте аккаунт, для доступа ко многим функциям. Так же актвация аккаунта позволит
|
||||
восстановить пароль.</p>
|
||||
<div style={{ display: "flex", justifyContent: "flex-end" }}>
|
||||
<Button onClick={this.sendActivateLink}
|
||||
style={{ fontSize: '1.2rem' }}>Активировать!</Button>
|
||||
<Button
|
||||
onClick={this.sendActivateLink}
|
||||
style={{ fontSize: '1.2rem' }}
|
||||
>
|
||||
Активировать!
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -101,22 +107,24 @@ class User extends React.Component<Props, State> {
|
||||
<div className="User__Info_Avatar"
|
||||
style={{ backgroundImage: `url(${user.avatar}?s=96&t=${new Date().getTime()})` }}/>
|
||||
<div>
|
||||
<div className='User__Info_Nick'>{user.username}</div>
|
||||
<div className='User__Info_Nick' style={userNameStyle}>{user.username}</div>
|
||||
<div className='User__Info_Group' style={{
|
||||
borderColor: user.group.color,
|
||||
color: user.group.color
|
||||
}}>{user.group.name}</div>
|
||||
borderColor: user.group.color
|
||||
}}>
|
||||
{user.group.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>) : (
|
||||
<div className={'User__Info_Main'}>
|
||||
<div className="User__Info_Avatar"
|
||||
style={{ backgroundImage: `url(${user.avatar}?s=96&t=${new Date().getTime()})` }}/>
|
||||
<div>
|
||||
<div className='User__Info_Nick'>{user.username}</div>
|
||||
<div className='User__Info_Nick' style={userNameStyle}>{user.username}</div>
|
||||
<div className='User__Info_Group' style={{
|
||||
borderColor: user.group.color,
|
||||
color: user.group.color
|
||||
}}>{user.group.name}</div>
|
||||
borderColor: user.group.color
|
||||
}}>
|
||||
{user.group.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -3,5 +3,8 @@ import currentUser from './currentUser'
|
||||
import modal from "./modal";
|
||||
import notifications from './notifications'
|
||||
import userSettings from './userSettings';
|
||||
import snowfall from './snowfall';
|
||||
|
||||
export default combineReducers({ currentUser, modal, notifications, userSettings })
|
||||
export default combineReducers({
|
||||
currentUser, modal, notifications, userSettings, snowfall
|
||||
});
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
const SET_SNOWFLAKE_COUNT = 'unmei/snowfall/SET_SNOWFLAKE_COUNT';
|
||||
const SET_SNOWFALL_STATUS = 'unmei/snowfall/SET_SNOWFALL_STATUS';
|
||||
|
||||
const initialState: Snowfall = {
|
||||
snowflakeCount: JSON.parse(localStorage.getItem('snowflakeCount') ?? '250'),
|
||||
snowfallStatus: true
|
||||
};
|
||||
|
||||
const reducer = (state = initialState, action: Action<Snowfall>) => {
|
||||
if(!action.payload) return state;
|
||||
const payload = action.payload;
|
||||
switch(action.type) {
|
||||
case SET_SNOWFLAKE_COUNT:
|
||||
localStorage.setItem('snowflakeCount', JSON.stringify(payload.snowflakeCount));
|
||||
return Object.assign({}, state, { snowflakeCount: payload.snowflakeCount });
|
||||
case SET_SNOWFALL_STATUS:
|
||||
return Object.assign({}, state, { snowfallStatus: payload.snowfallStatus });
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
const setSnowflakeCount = (snowflakeCount: number) => ({
|
||||
type: SET_SNOWFLAKE_COUNT,
|
||||
payload: {
|
||||
snowflakeCount
|
||||
}
|
||||
});
|
||||
const setSnowfallStatus = (snowfallStatus: boolean) => ({
|
||||
type: SET_SNOWFALL_STATUS,
|
||||
payload: {
|
||||
snowfallStatus
|
||||
}
|
||||
});
|
||||
|
||||
export default reducer;
|
||||
export { setSnowflakeCount, setSnowfallStatus }
|
||||
Vendored
+15
@@ -1,3 +1,18 @@
|
||||
type Theme = 'dark' | 'blue' | 'red' | 'green' | 'light' | 'win95';
|
||||
type ExitStatus = 'came_out';
|
||||
type NovelPlatform = 'win';
|
||||
|
||||
type ApiError = {
|
||||
code: number
|
||||
text: string
|
||||
}
|
||||
|
||||
type ApiResponse<T> = {
|
||||
error: boolean
|
||||
error_data?: ApiError | string
|
||||
data?: T
|
||||
}
|
||||
|
||||
type VersionResponse = {
|
||||
version: string
|
||||
}
|
||||
|
||||
Vendored
+8
@@ -1,8 +1,14 @@
|
||||
type Snowfall = {
|
||||
snowflakeCount: number
|
||||
snowfallStatus: boolean
|
||||
};
|
||||
|
||||
type StoreState = {
|
||||
currentUser: UserType
|
||||
userSettings: UserSettingsType
|
||||
notifications: React.ReactNode[]
|
||||
modal: React.ReactNode
|
||||
snowfall: Snowfall
|
||||
}
|
||||
|
||||
type Action<T> = {
|
||||
@@ -16,3 +22,5 @@ type SetSettings = (settings: UserSettingsType) => void
|
||||
type SetModal = (modal: React.ReactNode | null) => void
|
||||
type HideModal = () => void
|
||||
type AddNotification = (notification: React.ReactNode) => void
|
||||
type SetSnowflakeCount = (snowflakeCount: number) => void
|
||||
type SetSnowflakeStatus = (snowfallStatus: boolean) => void
|
||||
|
||||
Vendored
+18
-25
@@ -1,16 +1,14 @@
|
||||
type Theme = 'dark' | 'blue' | 'red' | 'green' | 'light'
|
||||
type ExitStatus = 'came_out'
|
||||
type NovelPlatform = 'win'
|
||||
|
||||
type ApiError = {
|
||||
code: number
|
||||
text: string
|
||||
}
|
||||
|
||||
type ApiResponse<T> = {
|
||||
error: boolean
|
||||
error_data?: ApiError | string
|
||||
data?: T
|
||||
type UserType = {
|
||||
authorized: boolean
|
||||
id: number
|
||||
username: string
|
||||
email?: string
|
||||
avatar: string
|
||||
cover: string
|
||||
group: UserGroupType
|
||||
is_superuser: boolean
|
||||
is_activated: boolean
|
||||
is_banned: boolean
|
||||
}
|
||||
|
||||
type NovelType = {
|
||||
@@ -36,18 +34,6 @@ type CharacterType = {
|
||||
main: boolean
|
||||
}
|
||||
|
||||
type UserType = {
|
||||
authorized: boolean
|
||||
id: number
|
||||
username: string
|
||||
email?: string
|
||||
avatar: string
|
||||
cover: string
|
||||
group: UserGroupType
|
||||
is_superuser: boolean
|
||||
is_activated: boolean
|
||||
}
|
||||
|
||||
type UserSettingsType = {
|
||||
avatar: string
|
||||
theme: Theme
|
||||
@@ -89,3 +75,10 @@ type PostType = {
|
||||
author: string
|
||||
author_id: number
|
||||
}
|
||||
|
||||
type ClubType = {
|
||||
id: number
|
||||
name: string
|
||||
avatar: string
|
||||
ownerId: number
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import classNames from "../../classNames";
|
||||
import './Group.sass'
|
||||
|
||||
interface Props extends HTMLAttributes<HTMLElement> {
|
||||
title: string
|
||||
title?: string
|
||||
direction?: 'column' | 'row'
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ const Group = (restProps: Props) => {
|
||||
}
|
||||
return (
|
||||
<div className={classNames('Group', className)} {...props}>
|
||||
<div className="Group__Title">{title}</div>
|
||||
{title && <div className="Group__Title">{title}</div>}
|
||||
<div style={style} className={'Group__Content'}>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user