diff --git a/src/Constants.sass b/src/Constants.sass index 25cd7f6..2ca10b5 100644 --- a/src/Constants.sass +++ b/src/Constants.sass @@ -6,7 +6,7 @@ $mainColor: #efefef $secondaryColor: #aaaaaa -$linkHoverColor: #dfdfdf +$linkHoverColor: #efefef // BLACK $primaryBackgroundColor: #121212 @@ -57,4 +57,4 @@ $blockPadding: .2rem $borderRadius: 4px -$novelBlockSize: calc(100% / 4 - 1rem) \ No newline at end of file +$novelBlockSize: calc(100% / 4 - 1rem) diff --git a/src/api/api.ts b/src/api/api.ts index 517818c..8de8146 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -11,7 +11,7 @@ async function request(url: string, method: string, body?: string | FormData) method, body, credentials: "include", headers: new Headers({ - 'Dev': /dev.(.+)/.test(window.location.href) ? '1' : '0' + 'Dev': /(dev\..+)/.test(window.location.href) ? '1' : '0' }) }); @@ -45,14 +45,18 @@ const TranslateStatus: { [id: string]: string } = { }; const TranslatePlatform: { [id: string]: string } = { - win: 'Windows' + win: 'Windows', + mac: 'macOS', + linux: 'Linux', + ios: 'IOS', + android: 'Andriod' }; const TranslateExitStatus: { [id: string]: string } = { came_out: 'Вышла' -} +}; export const getVersion = () => get('version'); -export const version = '1.1.1'; +export const version = '1.2'; export { get, post, put, del, TranslateStatus, TranslatePlatform, TranslateExitStatus }; diff --git a/src/api/clubs.ts b/src/api/clubs.ts index 32bc7dd..377058a 100644 --- a/src/api/clubs.ts +++ b/src/api/clubs.ts @@ -1,3 +1,4 @@ import { get } from "./api"; +export const getClubs = () => get('/clubs'); export const getClub = (id: number) => get(`/clubs/${id}`); diff --git a/src/pages/App/App.sass b/src/pages/App/App.sass index 80fda12..a8e3f4c 100644 --- a/src/pages/App/App.sass +++ b/src/pages/App/App.sass @@ -27,6 +27,8 @@ body --error-bg-color: #{$errorBackgroundColor} --success-bg-color: #{$successBackgroundColor} + --accent-color: #f22 + color: var(--main-color) background: var(--main-bg-color) diff --git a/src/pages/App/App.tsx b/src/pages/App/App.tsx index 3e49854..9dfb2e4 100644 --- a/src/pages/App/App.tsx +++ b/src/pages/App/App.tsx @@ -33,6 +33,7 @@ import Snowfall from "react-snowfall"; import eruda from 'eruda'; // @ts-ignore import parser from 'bbcode-to-react'; +import Clubs from "../Clubs/Clubs"; type Props = { notifications: React.ReactNode[] @@ -62,8 +63,21 @@ class App extends React.Component { const { setUser, setSettings } = this.props; const cachedTheme = localStorage.getItem('theme'); - if(cachedTheme) - document.body.setAttribute('theme', cachedTheme); + if(cachedTheme) { + if(cachedTheme === 'custom') { + const customTheme = localStorage.getItem('customTheme') ?? ''; + + customTheme.split('\n').forEach(line => { + const [name, value] = line.split(':'); + document.body.style.setProperty(name, value); + }); + } else + document.body.setAttribute('theme', cachedTheme); + } + + const accentColor = localStorage.getItem('accentColor'); + if(accentColor) + document.body.style.setProperty('--accent-color', accentColor); fetchCurrentUser().then(user => { setUser(user); @@ -85,7 +99,7 @@ class App extends React.Component { }); getVersion().then(res => { - if(version !== res.version) { + if(version !== res.version && !/(dev\..+)/.test(window.location.href)) { console.debug(`Current back-end version: ${res.version}. Clearing cache`); caches.keys().then(names => { @@ -93,6 +107,10 @@ class App extends React.Component { }).then(() => { window.location.reload(); }); + } else if(/(dev\..+)/.test(window.location.href)) { + caches.keys().then(names => { + names.forEach(name => caches.delete(name).catch(console.error)) + }); } }); } @@ -100,7 +118,7 @@ class App extends React.Component { render() { const { modal, snowfall } = this.props; const { user } = this.state; - const isBanned = user?.is_banned || JSON.parse(localStorage.getItem('banned') || 'false'); + const isBanned = user?.is_banned || JSON.parse(localStorage.getItem('banned') ?? 'false'); if(isBanned) { return ( @@ -109,12 +127,13 @@ class App extends React.Component { ); } + return ( <> + {snowfall.snowfallStatus && ( )} -
{this.props.notifications.map((n, i) => ( @@ -132,6 +151,7 @@ class App extends React.Component { + diff --git a/src/pages/Club/Club.tsx b/src/pages/Club/Club.tsx index fc1b0b0..73393b5 100644 --- a/src/pages/Club/Club.tsx +++ b/src/pages/Club/Club.tsx @@ -17,20 +17,22 @@ class Club extends React.Component { } componentDidMount() { - getClub(this.props.match.params.clubId).then(club => { + const { match } = this.props; + + getClub(match.params.clubId).then(club => { this.setState({ club }); }); } render() { - if(!this.state.club) return ; - return ( -
- + const { club } = this.state; + if(!club) return ; - -
- ) + return ( + + + + ); } } diff --git a/src/pages/Clubs/Clubs.tsx b/src/pages/Clubs/Clubs.tsx new file mode 100644 index 0000000..6d161ce --- /dev/null +++ b/src/pages/Clubs/Clubs.tsx @@ -0,0 +1,39 @@ +import React from "react"; +import { getClubs } from "../../api/clubs"; +import { Link } from "react-router-dom"; + +type Props = {} +type State = { + clubs: ClubType[] +} + +class Clubs extends React.Component { + state: State = { + clubs: [] + } + + componentDidMount() { + getClubs().then(clubs => { + this.setState({ clubs }); + }); + } + + render() { + const { clubs } = this.state; + + return ( +
+ {clubs.map(club => ( + +
+ +

{club.name}

+
+ + ))} +
+ ); + } +} + +export default Clubs; diff --git a/src/pages/Footer/Footer.tsx b/src/pages/Footer/Footer.tsx index 69cb8d5..753c879 100644 --- a/src/pages/Footer/Footer.tsx +++ b/src/pages/Footer/Footer.tsx @@ -30,7 +30,7 @@ const Footer: React.FC = () => {
- Unmei © 2019-2020 + Unmei © 2019-2021
); diff --git a/src/pages/Modals/ConfirmModal.tsx b/src/pages/Modals/ConfirmModal.tsx new file mode 100644 index 0000000..e006bde --- /dev/null +++ b/src/pages/Modals/ConfirmModal.tsx @@ -0,0 +1,35 @@ +import React, { CSSProperties } from "react"; +import Modal from "../../ui/Modal/Modal"; +import Button from "../../ui/Button/Button"; +import { useStore } from "react-redux"; +import { hideModal } from "../../store/ducks/modal"; + +type Props = { + onConfirm: () => void + onDeny?: () => void +} + +const ConfirmModal = ({ onConfirm, onDeny }: Props) => { + const buttonStyle: CSSProperties = { + width: '50%', boxSizing: "border-box", + margin: 6, fontSize: '1.2rem' + }; + + const store = useStore(); + + return ( + +
+ + +
+
+ ); +}; + +export default ConfirmModal; diff --git a/src/pages/Navbar/Navbar.sass b/src/pages/Navbar/Navbar.sass index 7081d56..71f1ec3 100644 --- a/src/pages/Navbar/Navbar.sass +++ b/src/pages/Navbar/Navbar.sass @@ -1,5 +1,3 @@ -$hoverColor: #f22 - .Minimized height: 3rem @@ -42,7 +40,7 @@ $hoverColor: #f22 bottom: 0 left: 0 height: 0 - background: $hoverColor + background: var(--accent-color) transition: heigth .1s ease-in -webkit-transition: height .1s ease-in @@ -97,7 +95,7 @@ $hoverColor: #f22 bottom: 0 left: 0 height: 0 - background: $hoverColor + background: var(--accent-color) transition: heigth .1s linear -webkit-transition: height .1s linear diff --git a/src/pages/Navbar/Navbar.tsx b/src/pages/Navbar/Navbar.tsx index a055e99..277f2f9 100644 --- a/src/pages/Navbar/Navbar.tsx +++ b/src/pages/Navbar/Navbar.tsx @@ -28,11 +28,12 @@ class Navbar extends React.Component { } logout = () => { - this.props.logout(); + const { logout } = this.props; + logout(); localStorage.removeItem('user'); localStorage.removeItem('theme'); document.body.removeAttribute('theme'); - userLogout().catch(() => {}); + userLogout().catch(); }; changeSize = () => { @@ -60,6 +61,9 @@ class Navbar extends React.Component { Новеллы + {/**/} + {/* Клубы*/} + {/**/} {expand && isMinimized && (
X
diff --git a/src/pages/News/AllNews.tsx b/src/pages/News/AllNews.tsx index 0d79227..90c32ef 100644 --- a/src/pages/News/AllNews.tsx +++ b/src/pages/News/AllNews.tsx @@ -30,7 +30,7 @@ class AllNews extends React.Component<{}, State> {
Новости
- {/*{}*/} + {/*{}*/} {news.map(post => ( diff --git a/src/pages/Novel/Novel.tsx b/src/pages/Novel/Novel.tsx index 50ab10d..3feecf3 100644 --- a/src/pages/Novel/Novel.tsx +++ b/src/pages/Novel/Novel.tsx @@ -15,10 +15,12 @@ import { } from "../../api/novels"; import { Link } from "react-router-dom"; import Loading from "../../ui/Loading"; -import { TranslateExitStatus, TranslateStatus } from "../../api/api"; +import { TranslateExitStatus, TranslatePlatform, TranslateStatus } from "../../api/api"; import Comments from "../Comments/Comments"; import LoadingModal from "../Modals/LoadingModal"; import { hideModal, setModal } from "../../store/ducks/modal"; +import { hasPermission } from "../../utils"; +import ConfirmModal from "../Modals/ConfirmModal"; type Props = { currentUser: UserType @@ -157,7 +159,7 @@ class Novel extends React.Component { render() { const { errorCode, novel, comments, characters, genres, userData, statusExpanded, hasMoreComments } = this.state; - const { currentUser } = this.props; + const { currentUser, setModal } = this.props; if(errorCode === 100) return ; if(!novel) return ; @@ -207,6 +209,13 @@ class Novel extends React.Component { ))} + + {hasPermission(currentUser, 'novel') && ( +
+
Изменить
+
setModal( console.log(123)}/>)}>Удалить
+
+ )} }
@@ -225,10 +234,18 @@ class Novel extends React.Component {
Статус: {TranslateExitStatus[novel.exit_status]}
-
+ {novel.duration > 0 &&
Продолжительность: {this.countNovelDuration(novel.duration)} +
} + {novel.platforms &&
+ Платформы: {novel.platforms.split(',').map(platform => TranslatePlatform[platform]).join(', ')} +
} + {novel.links.length > 0 &&
+ Ссылки: {novel.links.map(link => {link.name})} +
} +
+ Ср. оценка: {novel.rating.toFixed(2)}
-
Ср. оценка: {novel.rating.toFixed(2)}
{novel.description}
diff --git a/src/pages/User/Settings/Appearance.tsx b/src/pages/User/Settings/Appearance.tsx index 9355eba..1efae31 100644 --- a/src/pages/User/Settings/Appearance.tsx +++ b/src/pages/User/Settings/Appearance.tsx @@ -5,6 +5,8 @@ import { updateUserAppearanceSettings } from "../../../api/users"; import { connect } from "react-redux"; import { setUser } from "../../../store/ducks/currentUser"; import { setSnowflakeCount, setSnowfallStatus } from "../../../store/ducks/snowfall"; +import Input from "../../../ui/Input/Input"; +import TextField from "../../../ui/TextField/TextField"; type Props = { setUser: SetUser @@ -17,13 +19,16 @@ type Props = { type State = { theme: Theme + accentColor: string + customTheme: string colorName: boolean snowflakeCount: 0 } class Appearance extends React.Component { state: State = { - theme: this.props.settings.theme, + theme: this.props.settings.theme, accentColor: localStorage.getItem('accentColor') ?? '#f22', + customTheme: localStorage.getItem('customTheme') ?? '', colorName: JSON.parse(localStorage.getItem('color-name') || 'false'), snowflakeCount: JSON.parse(localStorage.getItem('snowflakeCount') ?? '250') } @@ -42,12 +47,33 @@ class Appearance extends React.Component { }); } + saveAccentColor = (event: FormEvent) => { + event.preventDefault(); + + const { accentColor } = this.state; + + localStorage.setItem('accentColor', accentColor); + document.body.style.setProperty('--accent-color', accentColor); + } + + saveCustomTheme = (event: FormEvent) => { + event.preventDefault(); + + const { customTheme } = this.state; + + localStorage.setItem('customTheme', customTheme); + customTheme.split('\n').forEach(line => { + const [name, value] = line.split(':'); + document.body.style.setProperty(name, value); + }); + } + render() { const { snowfallSettings, setSnowflakeCount, setSnowfallStatus } = this.props; - const { theme, colorName } = this.state; + const { theme, colorName, accentColor, customTheme } = this.state; return (<> - +
+ +
+ + +
+ +
+ this.setState({ customTheme: e.target.value })} + /> + +