});
};
render() {
this.episodes = [];
let episodeList = this.state.episodes.map(ep =>
<EpisodePanel
episode={ep}
key={ep.id}
history={this.props.history}
closeAllComments={this.closeAllComments}
ref={'episode' + ep.id}
/>
);
return (
<div>
{this.state.episodes.length === 0 && <WelcomePanel/>}
{episodeList}
</div>
)
}
}
Файл: Discasst\src\main\resources\static\app\components\podcastPage\ PodcastPage.js
import Navbar from '../Navbar';
import PodcastProfile from "./PodcastProfile";
import PodcastPageContent from './PodcastPageContent';
import {getCookie} from '../Helper';
const React = require('react');
export default class PodcastPage extends React.Component {
constructor(props) {
super(props);
this.state = {
podcast: null,
coauthors: [],
coauthorsInfo: [],
myPodcast: false
};
}
componentDidMount() {
this.loadPodcastInfo();
}
loadPodcastInfo() {
fetch(`/getPodcastById?id=${this.props.match.params.podcastId}`)
.then((response) => {
return response.json()
})
.then((json) => {
console.log(json);
this.setState({podcast: json});
this.loadCoauthors();
});
}
loadCoauthors() {
fetch(`/getCoauthors?podcastId=${this.props.match.params.podcastId}`)
.then((response) => {
return response.json()
})
.then((json) => {
let coauthorsIds = [this.state.podcast.authorId.toString(),...json.map(x => x.userId.toString())];
console.log(coauthorsIds)
this.setState({coauthors: coauthorsIds});
this.loadCoauthorsInfo(coauthorsIds);
this.myPodcastCheck();
});
}
myPodcastCheck() {
if (this.state.coauthors.indexOf(getCookie("userId")) !== -1) this.setState({myPodcast: true});
}
loadCoauthorsInfo(coauthorsIds) {
let data = {
"ids": coauthorsIds
};
$.ajax({
url: `/getCoauthorsByIdList`,
type: 'post',
contentType: 'application/json',
data: JSON.stringify(data),
success: (response) => {
console.log(response);
this.setState({coauthorsInfo: response});
},
error: (jqXhr, textStatus, errorThrown) => {
console.log(errorThrown + " " + textStatus);
}
});
}
render() {
return (
<div>
<Navbar history={this.props.history}/>
<div className="container-fluid">
{this.state.podcast !== null &&
<div style={{display: "flex",}}>
<div style={{width: "25%"}}>
<PodcastProfile podcast={this.state.podcast} coauthors={this.state.coauthorsInfo}
history={this.props.history}/>
</div>
<div style={{width: "50%"}}>
<PodcastPageContent
feed={false}
podcastId={this.state.podcast.id}
myPodcast={this.state.myPodcast}
history={this.props.history}
/>
</div>
<div style={{width: "25%"}}>
</div>
</div>}
</div>
</div>
)
}
}
Файл: Discasst\src\main\resources\static\app\components\podcastPage\ PodcastPageContent.js
import React, {Component} from 'react';
import {Button, ButtonGroup} from 'react-bootstrap';
import PodcastList from './PodcastList';
import TweetPage from './tweets/TweetPage';
export default class PodcastPageContent extends Component {
constructor() {
super();
this.state = {
tweetPage: false
}
}
onTweetPageClick = (e) => {
this.setState({tweetPage: true});
e.target.blur();
};
onPodcastPageClick = (e) => {
this.setState({tweetPage: false});
e.target.blur();
};
render() {
return (
<div style={{textAlign: "center"}}>
<ButtonGroup style={{marginBottom: 20}}>
<Button
onClick={this.onPodcastPageClick}
style={{width: 100}}
bsStyle={!this.state.tweetPage ? "info": "default"}>
Выпуски
</Button>
<Button
onClick={this.onTweetPageClick}
style={{width: 100}}
bsStyle={this.state.tweetPage ? "info": "default"}>
Микроблог
</Button>
</ButtonGroup>
{this.state.tweetPage ? <TweetPage
feed={this.props.feed}
podcastId={this.props.podcastId}
myPodcast={this.props.myPodcast}
history={this.props.history}
/>:
<PodcastList
feed={this.props.feed}
podcastId={this.props.podcastId}
history={this.props.history}
/>
}
</div>
);
}
}
Файл: Discasst\src\main\resources\static\app\components\podcastPage\ PodcastProfile.js
import {getCookie, getFullCSRFParam} from '../Helper';
const React = require('react');
export default class PodcastProfile extends React.Component {
constructor(props) {
super(props);
this.state = {
genres: null,
podcast: null,
myDecision: 0,
mySubscribe: false,
isUserSignedIn: false,
extraSub: 0,
emptyLinks: false
};
}
componentDidMount() {
const podcast = this.state.podcast === null ? this.props.podcast: this.state.podcast;
if (podcast.itunesLink === "" && podcast.vkLink === "" && podcast.twitterLink === "" && podcast.facebookLink === "") this.setState({emptyLinks: true})
this.isUserLogIn();
this.loadAllGenres();
}
loadAllGenres() {
fetch(`/getAllGenres`)
.then((response) => {
return response.json()
})
.then((json) => {
console.log(json)
this.setState({genres: json, loadData: this.state.loadData + 1});
});
}
ratingChange = (rating) => {
if (this.state.isUserSignedIn === false) {
this.props.history.push('/signin');
} else {
let obj = {
"podcastId": this.props.podcast.id,
"userId": getCookie('userId'),
"decision": rating,
};
let rate = JSON.stringify(obj);
const that = this;
$.ajax({
url: `/vote?${getFullCSRFParam()}`,
type: 'post',
contentType: 'application/json',
data: rate,
success: () => {
this.setState({myDecision: rating ? 1: -1});
this.loadNewPodcastInfo();
},
error: (jqXhr, textStatus, errorThrown) => {
console.log(errorThrown + " " + textStatus);
}
});
}
};
isUserLogIn() {
let username = getCookie("username");
if (username === "" || username === undefined) {
this.setState({isUserSignedIn: false});
return false;
}
fetch(`/loginCheck?${getFullCSRFParam()}`, {
method: 'POST'
}).then((response) => {
if (response.status === 200 && !response.redirected) {
this.setState({isUserSignedIn: true});
this.loadUserRatingAndSubscribeInfo();
return true;
} else {
this.setState({isUserSignedIn: false});
return false;
}
});
}
loadNewPodcastInfo() {
fetch(`/getPodcastById?id=${this.props.podcast.id}`)
.then((response) => {
return response.json()
})
.then((json) => {
this.setState({podcast: json});
});
}
loadUserRatingAndSubscribeInfo() {
fetch(`/getMyVote?userId=${getCookie("userId")}&podcastId=${this.props.podcast.id}`)
.then((response) => {
return response.json()
})
.then((json) => {
console.log(json);
this.setState({myDecision: json});
this.loadUserSubscribeInfo();
});
}
loadUserSubscribeInfo() {
fetch(`/getMySubscribe?userId=${getCookie("userId")}&podcastId=${this.props.podcast.id}`)
.then((response) => {
return response.json()
})
.then((json) => {
console.log(json);
this.setState({mySubscribe: json});
});
}
subscribeStateChange = () => {
if (this.state.isUserSignedIn === false) {
this.props.history.push('/signin');
} else {
fetch(`/subscribe?userId=${getCookie("userId")}&podcastId=${this.props.podcast.id}`)
.then((response) => {
if (response.status === 200) {
this.setState({mySubscribe: !this.state.mySubscribe})
this.loadNewPodcastInfo();
}
});
}
};
render() {
const podcast = this.state.podcast === null ? this.props.podcast: this.state.podcast;
return (
<div className="container-fluid">
<div className="panel panel-info">
<div className="panel-body" style={{maxWidth: "100%"}}>
<div className="row">
<div className="col-xs-4" style={{width: "180px", marginBottom: -20}}>
<div className="panel panel-info"
style={{width: "150px", textAlign: "center", paddingBottom: 10}}>
<br></br>
<img src="/images/podcastDefaultLogo.png"
className="img-rounded" style={{width: "120px"}}></img><br></br>
<div className="panel-body">
<button onClick={() => {
this.ratingChange(false)
}} type="button"
className={this.state.myDecision === -1 ? "btn btn-danger btn-xs": "btn btn-info btn-xs"}>
<span
className="glyphicon glyphicon-minus">
</span>
</button>
 <span className="badge">{podcast.rating}</span> 
<button onClick={() => {
this.ratingChange(true)
}} type="button"
className={this.state.myDecision === 1 ? "btn btn-success btn-xs": "btn btn-info btn-xs"}>
<span
className="glyphicon glyphicon-plus">
</span>
</button>
</div>
</div>
<div>
<button
type="button"
style={{width: "150px"}}
className={this.state.mySubscribe ? "btn btn-success btn-md": "btn btn-info btn-md"}
onClick={() => {
this.subscribeStateChange()
}}
>
{this.state.mySubscribe ? "Отписаться": "Подписаться"}
</button>
</div>
<br></br>
</div>
<div className="container-fluid">
<h4>{podcast.name}</h4>
<h4>
{this.state.genres !== null &&
<small>{this.state.genres[podcast.genreId].name}</small>}
</h4>
<hr></hr>
<p className="text-justify" style={{minHeight: 160}}>{podcast.description}</p>
<hr></hr>
<div className="row">
<div className="col-xs-6">
<h5 className="text-center">Подписчиков: {podcast.subscribersCount}</h5>
</div>
<div className="col-xs-6">
<h5 className="text-center">Выпусков: {podcast.episodeCount}</h5>
</div>
</div>
{!this.state.emptyLinks && <div>
<hr></hr>
</div>}
{!this.state.emptyLinks && <div className="container-fluid text-center">
{podcast.itunesLink !== "" && <a href={podcast.itunesLink}><img
src="/images/itunesLogo.png"
className="img-rounded" style={{width: "30px", margin: "0 10px 0 10px"}}></img></a>}
{podcast.vkLink !== "" && <a href={podcast.vkLink}><img
src="/images/vkLogo.png"
className="img-rounded" style={{width: "30px", margin: "0 10px 0 10px"}}></img></a>}
{podcast.twitterLink !== "" && <a href={podcast.twitterLink}><img
src="/images/twitterLogo.png"
className="img-rounded" style={{width: "30px", margin: "0 10px 0 10px"}}></img></a>}
{podcast.facebookLink !== "" && <a href={podcast.facebookLink}><img
src="/images/facebookLogo.png"
className="img-rounded" style={{width: "30px", margin: "0 10px 0 10px"}}></img>
</a>}
</div>}
<hr></hr>
<div>
<h5>Ведущие:</h5>
<div className="container-fluid text-center">
{this.props.coauthors.map(item =>
<a key={item.id} onClick={() => {
this.props.history.push(`/user/${item.username}`)
}}>
<h4>{item.name} (@{item.username})</h4>
</a>
)}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
)
}
}
Файл: Discasst\src\main\resources\static\app\components\podcastPage\tweets\ DeleteTweetButton.js
const React = require('react');
export default class Navbar extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<button type="button"
className="btn btn-info btn-xs pull-right"
onClick={this.props.onDelete}
style={{marginLeft: -24}}
>
<span className="glyphicon glyphicon-remove"></span>
</button>
)
}
}
Файл: Discasst\src\main\resources\static\app\components\podcastPage\tweets\ LoadingPanel.js
const React = require('react');
export default class LoadingPanel extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div hidden={this.props.hidden}>
<img className="center-block" src="/images/loading.svg" width="70" height="70"/>
<br/>
</div>
)
}
}
Файл: Discasst\src\main\resources\static\app\components\podcastPage\tweets\ Tweet.js
const React = require('react');
const url = '/deleteTweet?tweetId=';
import DeleteTweetButton from './DeleteTweetButton';
import {getCookie, getFullCSRFParam} from "../../Helper";
export default class Tweet extends React.Component {
constructor(props) {
super(props);
}
delete() {
const that = this;
const tweetId = this.props.tweet.id;
$.ajax({
url: url + tweetId + '&' + getFullCSRFParam(),
type: 'post',
success: () => {
that.props.callbackFromChild(tweetId);
},
error: (jqXhr, textStatus, errorThrown) => {
console.log(errorThrown + " " + textStatus);
}
});
}
render() {
const {text, creationDate, authorUsername} = this.props.tweet;
return (
<div className="container-fluid">
<div className="panel panel-info" style={{marginBottom: 10}}>
<div className="panel-body" style={{backgroundColor: '#ffffff'}}>
<div>
{this.props.myPodcast && getCookie("username") === authorUsername &&
<DeleteTweetButton onDelete={this.delete.bind(this)}/>}
<font size="3"><a onClick={() => {
this.props.history.push(`/user/${authorUsername}`)
}}>@{authorUsername}</a></font>
<h5 style={{color: '#acacac', marginBottom: 0}}>{creationDate}</h5>
</div>
<hr style={{margin: "10px 0 15px 0"}}/>
<font size="3" style={{whiteSpace: "pre-wrap"}}>{text}</font>
</div>
</div>
</div>
)
}
}
Файл: Discasst\src\main\resources\static\app\components\podcastPage\tweets\ TweetInput.js
import {getCookie, getFullCSRFParam} from "../../Helper";
const React = require('react');
const addTweetUrl = '/addTweet';
export default class TweetInput extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
}
handleSubmit = () => {
if (this.state.value !== '') {
this.addNewTweet();
}
};
addNewTweet() {
let obj = {
"text": this.state.value,
"authorUsername": getCookie("username"),
"authorId": getCookie("userId"),
"podcastId": this.props.podcastId
};
let tweetJson = JSON.stringify(obj);
const that = this;
$.ajax({
url: addTweetUrl + '?' + getFullCSRFParam(),
type: 'post',
contentType: 'application/json',
data: tweetJson,
success: () => {
that.afterPostNewTwit();
},
error: (jqXhr, textStatus, errorThrown) => {
console.log(errorThrown + " " + textStatus);
}
});
}
afterPostNewTwit() {
console.log("Tweet: " + this.state.value);
this.setState({value: ''});
this.refs.tweetTextarea.value = '';
this.refs.tweetTextarea.focus();
this.props.callbackFromChild();
}
handleChange = (event) => {
this.setState({value: event.target.value});
};
render() {
return (
<div className="container-fluid">
<div className="panel panel-info">
<div className="panel-body">
<div className="form-group shadow-textarea">
<textarea style={{resize: 'none'}} className="form-control z-depth-1"
rows="4" maxLength="200" autoFocus ref="tweetTextarea"
placeholder="What's happening?" onChange={this.handleChange}>
</textarea>
</div>
<div className="btn-group-md">
<button onClick={this.handleSubmit} type="button" className="btn btn-info pull-right"
style={{width: 100}}>Tweet
</button>
</div>
</div>
</div>
</div>
)
}
}
Файл: Discasst\src\main\resources\static\app\components\podcastPage\tweets\ TweetList.js
const React = require('react');
import Tweet from './Tweet';
export default class TweetList extends React.Component {
constructor(props) {
super(props);
}
deleteTwitCallback(id) {
this.props.callbackFromChild(id);
}
render() {
let tweets = this.props.tweets.map(tweet =>
<Tweet id={"tweet" + tweet.id}
key={"tweet" + tweet.id}
tweet={tweet}
callbackFromChild={this.deleteTwitCallback.bind(this)}
myPodcast={this.props.myPodcast}
history={this.props.history}
/>
);
const date = new Date();
console.log("Rerender TweetList at time: " + date.getMinutes() + "." + date.getSeconds() + "." + date.getMilliseconds());
return (
<div>
{tweets}
</div>
)
}
}
Файл: Discasst\src\main\resources\static\app\components\podcastPage\tweets\ TweetPage.js
const React = require('react');
import TweetList from './TweetList';
import TweetInput from './TweetInput';
import WelcomePanel from './WelcomePanel';
import LoadingPanel from './LoadingPanel';
import {getCookie} from '../../Helper';
export default class TweetPage extends React.Component {
constructor(props) {
super(props);
this.state = {
tweets: [],
currPage: 0,
pageSize: 10,
allPages: false,
numberOfTweets: 0,
loading: false,
loadImg: null
};
}
componentDidMount() {
this.getNewPageOfTweetsFromServer(0, this.state.pageSize * (this.state.currPage + 1));
const that = this;
/**
* Проверяет конец страницы, если так и страница не последняя, то грузит новую страницу твитов.
*/
window.onscroll = () => {
if (!that.state.allPages && !that.state.loading) {
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.getNewPageOfTweetsFromServer(nextPage, that.state.pageSize);
}
}
};
}
/**
* Грузит с сервера последний твит, помещает его в массив твитов, удаляет последний элемент массива
* если не последняя страница и ререндерит компонент.
*/
refreshTweetArrayAfterAdd() {
fetch(`${this.props.feed ? "/getAllTwitsByUserIdFeed": "/getAllTwitsByPodcastId"}?page=0&size=1&id=${this.props.feed ? getCookie('userId'): this.props.podcastId}`)
.then((response) => {
return response.json()
})
.then((json) => {
let array = this.state.tweets;
if (!this.state.allPages) array.splice(array.length - 1, 1);
this.setState({tweets: json.content.concat(array)});
});
}
/**
* Загружает заново с сервера все страницы и ререндерит компонент.
* @param id айдишник удаляемого твита.
*/
refreshTweetArrayAfterDelete(id) {
fetch(`${this.props.feed ? "/getAllTwitsByUserIdFeed": "/getAllTwitsByPodcastId"}?page=0&size=${(this.state.currPage + 1) * this.state.pageSize}&id=${this.props.feed ? getCookie('userId'): this.props.podcastId}`)
.then((response) => {
return response.json()
})
.then((json) => {
this.setState({tweets: json.content});
const date = new Date();