}
}
const el = document.getElementById('root');
ReactDOM.render(<App/>, el);
Файл: Discasst\src\main\resources\static\app\components\Helper.js
export function getCookie(name) {
var matches = document.cookie.match(new RegExp(
"(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)"
));
return matches ? decodeURIComponent(matches[1]): undefined;
}
export function getFullCSRFParam() {
const token = getCookie('XSRF-TOKEN');
return `_csrf=${token}`;
}
export function saveCookie(cookieName, value) {
document.cookie = `${cookieName}=${value}; path=/`;
}
export function deleteCookie(cookieName) {
document.cookie = `${cookieName}=${""}; path=/; expires=-1`;
}
export function saveAllCookie(username, id, email, creationDate, name, bio) {
saveCookie("username", username);
saveCookie("userId", id);
saveCookie("userEmail", email);
saveCookie("userCreationDate", creationDate);
saveCookie("name", name);
saveCookie("bio", bio);
}
export function deleteAllCookie() {
deleteCookie("username");
deleteCookie("userId");
deleteCookie("userEmail");
deleteCookie("userCreationDate");
deleteCookie("name");
deleteCookie("bio");
deleteCookie("XSRF-TOKEN");
}
export function isUserSignedIn() {
let username = getCookie("username");
return (username !== "" && username !== undefined);
}
export const getGenres = () =>
fetch("/getAllGenres").then(response => response.json());
Файл: Discasst\src\main\resources\static\app\components\Navbar.js
const React = require('react');
import {deleteAllCookie, getCookie, getFullCSRFParam} from "./Helper";
export default class Navbar extends React.Component {
constructor(props) {
super(props);
this.state = {
isSignedIn: false
};
}
componentDidMount() {
this.isUserLogIn();
}
loginout = () => {
if (this.state.isSignedIn) {
deleteAllCookie();
this.props.history.push(`/signin?logout`);
}
else {
this.props.history.push(`/signin`);
}
};
toStats = () => {
this.props.history.push(`/statistics`);
};
toMyProfile = () => {
this.props.history.push(`/user/${getCookie("username")}`);
};
toFeed = () => {
this.props.history.push(`/feed`);
};
toNotifications = () => {
this.props.history.push(`/notifications`);
};
toHome = () => {
this.props.history.push(`/`);
};
toSignIn = () => {
this.props.history.push(`/signin`);
};
isUserLogIn() {
let username = getCookie("username");
if (username === "" || username === undefined) return false;
fetch(`/loginCheck?${getFullCSRFParam()}`, {
method: 'POST'
}).then((response) => {
if (response.status === 200 && !response.redirected) this.setState({isSignedIn: true});
});
}
onFilterSubmit = () => {
const filter = document.getElementById("topSearch").value;
this.props.history.push({
pathname: '/search',
state: {filterValue: filter}
})
};
render() {
return (
<div>
<nav className="navbar navbar-default" style={{backgroundColor: '#ffffff'}} role="navigation">
<div className="container-fluid centered">
<div className="navbar-header">
<a onClick={() => {
this.toHome()
}} className="navbar-brand" style={{height: '64px', display: 'flex', alignItems: 'center'}}>
<img src="/images/discasst.png" height='36'></img>
</a>
</div>
<div>
<ul className="nav navbar-nav"
style={{height: '64px', display: 'flex', alignItems: 'center'}}>
<li><a onClick={() => {
!this.state.isSignedIn ? this.toSignIn(): this.toFeed()
}}>
<span className="glyphicon glyphicon-align-justify"></span> Feed</a>
</li>
<li><a onClick={() => {
!this.state.isSignedIn ? this.toSignIn(): this.toMyProfile()
}}>
<span className="glyphicon glyphicon-user"></span> My Profile</a>
</li>
<li><a onClick={() => {
this.toStats()
}}>
<span className="glyphicon glyphicon-stats"></span></a>
</li>
</ul>
{!this.props.isSearchOff && <ul className="nav navbar-nav navbar-center">
<li>
<a>
<form onSubmit={this.onFilterSubmit}>
<div className="input-group" style={{width: '300px'}}>
<input id="topSearch" type="text" className="form-control"/>
<span className="input-group-btn">
<button className="btn btn-info" type="button"
onClick={this.onFilterSubmit}
>
<span className="glyphicon glyphicon-search"></span>
</button>
</span>
</div>
</form>
</a>
</li>
</ul>}
<ul className="nav navbar-nav navbar-right">
<li>
<a><button className="btn btn-info" type="button" onClick={this.loginout}>
<span
className="glyphicon glyphicon-log-out"></span> {this.state.isSignedIn ? "Sign Out": "Sign In"}
</button></a>
</li>
</ul>
</div>
</div>
</nav>
</div>
)
}
}
Файл: Discasst\src\main\resources\static\app\components\feedPage\FeedPage.js
import Navbar from '../Navbar';
import PodcastPageContent from "../podcastPage/PodcastPageContent";
const React = require('react');
export default class FeedPage extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<div>
<Navbar history={this.props.history}/>
<div className="container-fluid">
<div style={{display: "flex",}}>
<div style={{width: "25%"}}>
</div>
<div style={{width: "50%", textAlign: "center"}}>
<PodcastPageContent
history={this.props.history}
feed={true}
/>
</div>
<div style={{width: "25%"}}>
</div>
</div>
</div>
</div>
)
}
}
Файл: Discasst\src\main\resources\static\app\components\login\RegisterPage.js
const React = require('react');
import {getFullCSRFParam} from "../Helper";
var errorEnum = {
NONE: "",
UNKNOWN: "Unknown error. Try again.",
USERNAME_TAKEN: "This login is already taken.",
LONG_USERNAME: "Login is too long (max length: 16).",
LONG_NAME: "Name is too long (max length: 16).",
WRONG_NAME: "Wrong name (allowed characters: a-Z, а-Я, 0-9, ' ').",
WRONG_USERNAME: "Wrong login (allowed characters: a-Z, 0-9, _).",
WRONG_EMAIL: "Wrong email (exemple: abc-42@xyz.com).",
WRONG_PASS: "Wrong password (allowed characters: a-Z, а-Я, 0-9).",
WRONG_PASS_LENGTH: "Password length must be 4-20 characters.",
WRONG_PASS_REPEAT: "Passwords don't match.",
};
export default class RegisterPage extends React.Component {
constructor(props) {
super(props);
this.state = {
password: '',
username: '',
name: '',
email: '',
success: false,
error: errorEnum.NONE
};
}
componentDidMount() {
$('form').submit(false);
}
onChangeUsername(event) {
this.setState({username: event.target.value});
}
onChangePass(event) {
this.setState({password: event.target.value});
}
onChangeName(event) {
this.setState({name: event.target.value});
}
onChangeEmail(event) {
this.setState({email: event.target.value});
}
register() {
if (!this.fullCheckPass()) return;
fetch(`/addUser?` +
`username=${this.state.username}` + `&` +
`password=${this.state.password}` + `&` +
`name=${this.state.name}` + `&` +
`email=${this.state.email}` + `&` +
getFullCSRFParam(), {
method: 'POST'
}).then((response) => {
let status = response.headers.get("newUserStatus");
if (status === "ok") {
this.setState({success: true, error: errorEnum.NONE});
setTimeout(() => {
this.props.history.push(`/signin`);
}, 1500)
}
else if (status === "usernameError") {
this.setState({success: false, error: errorEnum.USERNAME_TAKEN});
document.getElementById("login").focus();
}
else this.setState({success: false, error: errorEnum.UNKNOWN});
});
}
fullCheckPass() {
this.setState({error: errorEnum.NONE});
if (!this.validUsername()) return false;
if (!this.validName()) return false;
if (!this.validPassword()) return false;
if (!this.validEmail()) return false;
return true;
}
validUsername() {
var usernameRegex = /^[a-zA-Z0-9_]+$/;
var login = this.state.username;
if (login.length > 16) {
this.setState({error: errorEnum.LONG_USERNAME});
document.getElementById("login").focus();
return false;
}
var res = login.match(usernameRegex);
if (res === null) {
this.setState({error: errorEnum.WRONG_USERNAME});
document.getElementById("login").focus();
return false;
}
return true;
}
validPassword() {
var passRegex = /^[a-zA-Zа-яА-Я0-9]+$/;
var pass = this.state.password;
if (pass.length > 20 || pass.length < 4) {
this.setState({error: errorEnum.WRONG_PASS_LENGTH});
document.getElementById("pass").focus();
return false;
}
var res = pass.match(passRegex);
if (res === null) {
this.setState({error: errorEnum.WRONG_PASS});
document.getElementById("pass").focus();
return false;
}
return true;
}
validName() {
var nameRegex = /^[a-zA-Zа-яА-Я0-9 ]+$/;
var name = this.state.name;
if (name.length > 16) {
this.setState({error: errorEnum.LONG_NAME});
document.getElementById("name").focus();
return false;
}
var res = name.match(nameRegex);
if (res === null) {
this.setState({error: errorEnum.WRONG_NAME});
document.getElementById("name").focus();
return false;
}
return true;
}
validEmail() {
var emailRegex = /^[a-z0-9-\\.]+@[a-z-]+.[a-z]+$/;
var email = this.state.email;
var res = email.match(emailRegex);
if (res === null) {
this.setState({error: errorEnum.WRONG_EMAIL});
document.getElementById("email").focus();
return false;
}
return true;
}
render() {
const tss = this.state.success;
const tse = this.state.error;
return (
<div className="center-block main-center" style={{maxWidth: 500}}>
<hr className="hr100"/>
<div className="panel panel-info">
<div className="panel-body center-block" style={{maxWidth: 400}}>
<form onSubmit={this.register.bind(this)} className="form-signin">
<hr className="hr10"/>
<div className="input-group">
<span className="input-group-addon">Login</span>
<input id="login" className="form-control" required autoFocus
onChange={this.onChangeUsername.bind(this)}/>
</div>
<hr className="hr15"/>
<div className="input-group">
<span className="input-group-addon">Password</span>
<input id="pass" type="password" className="form-control" required
onChange={this.onChangePass.bind(this)}/>
</div>
<hr className="hr15"/>
<div className="input-group">
<span className="input-group-addon">Name</span>
<input id="name" className="form-control" required
onChange={this.onChangeName.bind(this)}/>
</div>
<hr className="hr15"/>
<div className="input-group">
<span className="input-group-addon">Email</span>
<input id="email" className="form-control" required
onChange={this.onChangeEmail.bind(this)}/>
</div>
<hr className="hr15"/>
{!tss && <button type="submit" className="btn btn-lg btn-info btn-block">Register</button>}
{tss && <button type="button" className="btn btn-lg btn-info btn-block"
disabled>Register</button>}
<hr className="hr10"/>
{tss &&
<div className="alert alert-success text-center">You successfully registered. Go to Sign
in...</div>}
{tse === errorEnum.USERNAME_TAKEN &&
<div className="alert alert-danger text-center">{errorEnum.USERNAME_TAKEN}</div>}
{tse === errorEnum.UNKNOWN &&
<div className="alert alert-danger text-center">{errorEnum.UNKNOWN}</div>}
{tse === errorEnum.LONG_USERNAME &&
<div className="alert alert-danger text-center">{errorEnum.LONG_USERNAME}</div>}
{tse === errorEnum.WRONG_USERNAME &&
<div className="alert alert-danger text-center">{errorEnum.WRONG_USERNAME}</div>}
{tse === errorEnum.LONG_NAME &&
<div className="alert alert-danger text-center">{errorEnum.LONG_NAME}</div>}
{tse === errorEnum.WRONG_NAME &&
<div className="alert alert-danger text-center">{errorEnum.WRONG_NAME}</div>}
{tse === errorEnum.WRONG_EMAIL &&
<div className="alert alert-danger text-center">{errorEnum.WRONG_EMAIL}</div>}
{tse === errorEnum.WRONG_PASS &&
<div className="alert alert-danger text-center">{errorEnum.WRONG_PASS}</div>}
{tse === errorEnum.WRONG_PASS_REPEAT &&
<div className="alert alert-danger text-center">{errorEnum.WRONG_PASS_REPEAT}</div>}
{tse === errorEnum.WRONG_PASS_LENGTH &&
<div className="alert alert-danger text-center">{errorEnum.WRONG_PASS_LENGTH}</div>}
</form>
</div>
</div>
</div>
)
}
}
Файл: Discasst\src\main\resources\static\app\components\login\SigninPage.js
const React = require('react');
import {getFullCSRFParam, saveAllCookie} from "../Helper";
export default class SigninPage extends React.Component {
constructor(props) {
super(props);
this.state = {
password: '',
username: '',
logout: false,
error: false
};
}
refreshPageParams(url) {
this.setState({
logout: false,
error: false
});
if (url.searchParams.get("error") !== null) this.setState({error: true});
if (url.searchParams.get("logout") !== null) this.setState({logout: true});
if (!this.state.error && !this.state.logout) return false;
}
componentDidMount() {
let url = new URL(window.location.href);
this.refreshPageParams(url);
$('form').submit(false);
}
signin() {
fetch(`/perform_login?` +
`username=${this.state.username}` + `&` +
`password=${this.state.password}` + `&` +
getFullCSRFParam(), {
method: 'POST'
}).then((response) => {
if (!this.refreshPageParams(new URL(response.url))) {
if (!this.state.error)
this.loadUserInfoFromServerAndRedirect();
}
});
}
onChangePass(event) {
this.setState({password: event.target.value});
}
onChangeUsername(event) {
this.setState({username: event.target.value});
}
loadUserInfoFromServerAndRedirect() {
$.getJSON(`/getUserInfo?username=${this.state.username}`, (data) => {
saveAllCookie(data.username, data.id, data.email, data.creationDate, data.name, data.bio)
}).then(() => {
this.props.history.push('/');
});
}
toRegiser = () => {
this.props.history.push('/register');
};
render() {
return (
<div className="center-block main-center signin-css">
<form onSubmit={this.signin.bind(this)} className="form-signin center-block" style={{maxWidth: 320}}>
<hr className="hr100"/>
<hr className="hr60"/>
<img src="/images/discasst.png" type="image" alt="" width="260" className="center-block"></img>
<hr className="hr15"/>
<input type="username" className="form-control form-control-signin-css" placeholder="Username"
required id='signinUser'
autoFocus onChange={this.onChangeUsername.bind(this)}></input>
<input type="password" className="form-control form-control-signin-css" placeholder="Password"
required id='signinPass'
onChange={this.onChangePass.bind(this)}></input>
<br/>
<button type="submit" className="btn btn-lg btn-info btn-block">Sign In</button>
<br/>
<button type="button" className="btn btn-lg btn-info btn-block" onClick={() => {
this.toRegiser()
}}>Sign Up
</button>
<br/>
{this.state.logout &&
<div className="alert alert-info text-center">You signed out successfully.</div>}
{this.state.error &&
<div className="alert alert-danger text-center">Invalid Username or Password.</div>}
</form>
</div>
)
}
}
Файл: Discasst\src\main\resources\static\app\components\podcastPage\ EpisodePanel.js
import React from 'react';
import ReactDisqusComments from 'react-disqus-comments';
export default class EpisodePanel extends React.Component {
constructor() {
super();
this.state = {
isCommentsOn: false
}
}
closeComments = () => {
this.setState({isCommentsOn: false});
};
render() {
return (
<div className="container-fluid">
<div className="panel panel-info" style={{marginBottom: 10}}>
<div className="panel-body" style={{maxWidth: '100%'}}>
<h4 style={{marginTop: 0}}>{this.props.episode.name}</h4>
<h5 style={{color: '#acacac', marginBottom: 0}}>{this.props.episode.date}</h5>
<hr style={{marginTop: 10}}/>
<iframe width="100%" height="63" src={`${this.props.episode.podsterLink}/embed/15?link=0&ap=0`}
frameborder="0" allowtransparency="true"></iframe>
<p className="text-justify">{this.props.episode.description}
</p>
<button
onClick={() => {
if (!this.state.isCommentsOn) this.props.closeAllComments();
this.setState({isCommentsOn: !this.state.isCommentsOn});
}}
type="button"
className="btn btn-info btn-md pull-right "
>
{this.state.isCommentsOn ? 'Свернуть комментарии': 'Обсудить'}
</button>
{this.state.isCommentsOn &&
<ReactDisqusComments
shortname={'discasst'}
identifier={'http://discasst.com/#!/episode/' + this.props.episode.id}
title={this.props.episode.name}
url={'http://discasst.com/#!/episode/' + this.props.episode.id}
onNewComment={() => {
}}
id={this.props.episode.id}
/>
}
</div>
</div>
</div>
);
}
}
Файл: Discasst\src\main\resources\static\app\components\podcastPage\ PodcastList.js
import React from 'react';
import EpisodePanel from './EpisodePanel';
import WelcomePanel from './tweets/WelcomePanel';
import {getCookie} from '../Helper';
export default class PodcastList extends React.Component {
constructor() {
super();
this.state = {
episodes: [],
pageSize: 5,
loading: false,
currPage: 0,
allPages: false,
};
}
componentDidMount() {
this.getEpisodes(0);
const that = this;
window.onscroll = () => {
if (!that.state.allPages && !that.state.loading) {
//(document.body.scrollHeight - window.scrollY - document.body.offsetHeight) <= 100
if ((window.innerHeight + window.scrollY) >= (document.body.offsetHeight - 100)) {
let nextPage = that.state.currPage + 1;
console.log("End of page, load next page: " + nextPage);
that.setState({currPage: nextPage, loading: true});
that.getEpisodes(nextPage);
}
}
};
}
getEpisodes(page) {
$.ajax({
url: `${this.props.feed ? "/getEpisodesInFeedByUserIdInPage": "/getEpisodesByPodcastIdInPage"}?id=${this.props.feed ? getCookie('userId'): this.props.podcastId}&page=${page}&size=${this.state.pageSize}`,
type: 'get',
contentType: 'application/json',
success: (response) => {
this.setState(
{
allPages: response.last,
loading: false,
episodes: this.state.episodes.concat(response.content)
}
);
},
error: (jqXhr, textStatus, errorThrown) => {
console.log(errorThrown + " " + textStatus);
}
});
}
closeAllComments = () => {
this.state.episodes.map((ep) => {
this.refs['episode' + ep.id].closeComments();