Социальная подкаст-платформа

Рассмотрение сервисов, позволяющих прослушивать подкасты. Разработка платформы, сочетающей в себе функции для удобного поиска подкастов и возможность их обсуждения с ведущими и другими слушателями. Оценка удобства работы с пользовательским интерфейсом.

Рубрика Программирование, компьютеры и кибернетика
Вид дипломная работа
Язык русский
Дата добавления 14.12.2019
Размер файла 2,7 M

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

{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();

});

};

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>

&#160;<span className="badge">{podcast.rating}</span>&#160;

<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();

console.log("Deleting tweet at time: " + date.getMinutes() + "." + date.getSeconds() + "." + date.getMilliseconds());

this.lastPageCheck(json);

});

}

/**

* Получает новую страницу твитов, добавляет её к уже существующему массиву твитов и ререндерит компонент.

*/

getNewPageOfTweetsFromServer(page, pagesize) {

fetch(`${this.props.feed ? "/getAllTwitsByUserIdFeed": "/getAllTwitsByPodcastId"}?page=${page}&size=${pagesize}&id=${this.props.feed ? getCookie('userId'): this.props.podcastId}`)

.then((response) => {

return response.json()

})

.then((json) => {

this.setState({tweets: this.state.tweets.concat(json.content)});

this.setState({numberOfTweets: json.totalElements});

this.lastPageCheck(json);

this.setState({loading: false});

});

}

lastPageCheck(json) {

this.setState({allPages: json.last});

}

addTweetCallback() {

this.refreshTweetArrayAfterAdd();

this.setState({numberOfTweets: (this.state.numberOfTweets + 1)});

}

deleteTwitCallback(id) {

this.refreshTweetArrayAfterDelete(id);

this.setState({numberOfTweets: (this.state.numberOfTweets - 1)});

}

render() {

return (

<div>

{this.props.myPodcast &&

<TweetInput podcastId={this.props.podcastId} callbackFromChild={this.addTweetCallback.bind(this)}/>}

<TweetList

tweets={this.state.tweets}

callbackFromChild={this.deleteTwitCallback.bind(this)}

myPodcast={this.props.myPodcast}

history={this.props.history}

/>

{!this.state.loading && this.state.numberOfTweets === 0 && <WelcomePanel/>}

<LoadingPanel hidden={!this.state.loading}/>

</div>

)

}

}

Файл: Discasst\src\main\resources\static\app\components\podcastPage\tweets\ WelcomePanel.js

const React = require('react');

export default class WelcomePanel extends React.Component {

render() {

return (

<div className="container-fluid">

<div className="panel panel-info" style={{height: '240px'}}>

<div className="panel-body text-center">

<h3 style={{color: '#a0a0a0'}}>Здесь еще ничего нет,</h3>

<h3 style={{color: '#a0a0a0'}}>но скоро обязательно что-то появится!</h3>

<br/>

<img src="/images/smile.png" width="60"/>

</div>

</div>

</div>

)

}

}

Файл: Discasst\src\main\resources\static\app\components\searchPage\PodcastLine.js

const React = require('react');

export default class PodcastLine extends React.Component {

constructor(props) {

super(props);

this.state = {

podcast: null

};

}

render() {

const podcast = this.state.podcast === null ? this.props.podcast: this.state.podcast;

return (

<div className="container-fluid">

<div className="panel panel-info" style={{marginBottom: 10}}>

<div className="panel-body">

<div className="row">

<div className="col-xs-4" style={{width: "180px"}}>

<div className="panel panel-info"

style={{width: "150px", textAlign: "center", marginBottom: 0}}>

<br></br>

<img src="images/podcastDefaultLogo.png"

className="img-rounded" style={{width: "120px"}}></img><br></br>

<div className="panel-body">

<span className="badge">{podcast.rating}</span>

</div>

</div>

</div>

<div className="container-fluid">

<h4>{podcast.name}

<small> {this.props.genres[podcast.genreId].name}</small>

</h4>

<hr/>

<p className="text-justify">{podcast.description}

</p>

<a>

<button type="button"

className="btn btn-info btn-md pull-right "

onClick={() => {

this.props.history.push(`/podcast/${podcast.id}`);

}}

>Подробнее

</button>

</a>

</div>

</div>

</div>

</div>

</div>

)

}

}

Файл: Discasst\src\main\resources\static\app\components\searchPage\PodcastList.js

const React = require('react');

import PodcastLine from "./PodcastLine";

export default class PodcastList extends React.Component {

constructor(props) {

super(props);

}

render() {

let podcastList = this.props.podcasts.map(podcast =>

<PodcastLine

history={this.props.history}

key={podcast.id}

podcast={podcast}

genres={this.props.genres}/>

);

return (

<div>

{podcastList}

</div>

)

}

}

Файл: Discasst\src\main\resources\static\app\components\searchPage\SearchPage.js

import PodcastList from "./PodcastList";

import Navbar from '../Navbar';

import SearchPanel from './SearchPanel';

import {getGenres} from "../Helper";

const React = require('react');

export default class SearchPage extends React.Component {

constructor(props) {

super(props);

this.state = {

podcasts: [],

genres: [],

loadData: 0,

pageSize: 5,

loading: false,

currPage: 0,

allPages: false,

filter: "",

notInGenres: [],

byRating: false

};

}

componentDidMount() {

const {state} = this.props.location;

if (state) {

this.loadPodcasts(state.filterValue, [], false, 0);

} else {

this.loadPodcasts("", [], false, 0);

}

this.loadAllGenres();

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.loadPodcasts(this.state.filter, this.state.notInGenres, this.state.byRating, nextPage);

}

}

};

}

loadAllGenres() {

getGenres().then(genres => {

this.setState({genres: genres, loadData: this.state.loadData + 1});

})

}

loadPodcasts(filter, notInGenres, byRating, page) {

let data = {

"filter": filter,

"genres": notInGenres.length === 0 ? [-1]: notInGenres.map(i => i - 1),

"byRating": byRating

};

$.ajax({

url: `/getPodcastsInPage?page=${page}&size=${this.state.pageSize}`,

type: 'post',

contentType: 'application/json',

data: JSON.stringify(data),

success: (response) => {

console.log(response);

this.setState(

page !== 0 ? {

podcasts: this.state.podcasts.concat(response.content),

loading: false,

allPages: response.last,

filter: filter,

notInGenres: notInGenres,

byRating: byRating

}:

{

podcasts: response.content,

loading: false,

allPages: response.last,

filter: filter,

notInGenres: notInGenres,

byRating: byRating,

currPage: 0

}

);

},

error: (jqXhr, textStatus, errorThrown) => {

console.log(errorThrown + " " + textStatus);

}

});

}

submitFilter = (filter, notInGenres, byRating) => {

this.loadPodcasts(filter, notInGenres, byRating, 0);

};

render() {

return (

<div>

<Navbar isSearchOff={true} history={this.props.history}/>

<div className="container-fluid">

<div style={{display: "flex",}}>

<div style={{width: "25%"}}>

<SearchPanel genres={this.state.genres}

onSubmit={(filter, notInGenres, byRating) => this.submitFilter(filter, notInGenres, byRating)}

filterValue={this.props.location.state ? this.props.location.state.filterValue: ""}

/>

</div>

<div style={{width: "50%"}}>

{this.state.loadData === 1 &&

<PodcastList history={this.props.history} podcasts={this.state.podcasts}

genres={this.state.genres}/>}

</div>

<div style={{width: "25%"}}>

</div>

</div>

</div>

</div>

)

}

}

Файл: Discasst\src\main\resources\static\app\components\searchPage\SearchPanel.js

import React, {Component} from 'react';

import {

Button,

ButtonGroup,

FormControl,

FormGroup,

Glyphicon,

InputGroup,

ListGroup,

ListGroupItem

} from 'react-bootstrap';

export default class SearchPanel extends Component {

constructor() {

super();

this.state = {

filterValue: "",

unselectedGenres: [],

byRating: false

}

}

componentDidMount() {

this.setState({filterValue: this.props.filterValue});

}

clearFilter = (e) => {

this.setState({filterValue: ""});

e.target.blur();

this.props.onSubmit("", this.state.unselectedGenres, this.state.byRating);

};

applyFilter = (e) => {

e.preventDefault();

this.props.onSubmit(this.state.filterValue, this.state.unselectedGenres, this.state.byRating);

};

handleChange = (e) => {

this.setState({filterValue: e.target.value});

};

unselectGenre = (id) => {

let tmp = this.state.unselectedGenres;

let index = tmp.indexOf(id);

index === -1 ? tmp = [...tmp, id]: tmp.splice(index, 1);

this.setState({unselectedGenres: tmp});

};

searchHeight = 40;

changeByRatingVal = (val) => {

this.setState({byRating: val});

this.props.onSubmit(this.state.filterValue, this.state.unselectedGenres, val);

};

render() {

return (

<div className="container-fluid">

<div className="panel panel-info">

<div className="panel-body">

<form

onSubmit={(event) => {

this.applyFilter(event)

}}

>

<FormGroup>

<InputGroup>

<FormControl

type="text"

placeholder="Search"

value={this.state.filterValue}

id="searchPanelFilter"

onChange={this.handleChange}

style={{height: this.searchHeight}}

/>

<InputGroup.Button>

<Button onClick={this.clearFilter}

style={{height: this.searchHeight, minWidth: this.searchHeight}}>

<Glyphicon glyph="remove" style={{fontSize: "1.2em"}}/>

</Button>

</InputGroup.Button>

</InputGroup>

</FormGroup>

</form>

<ButtonGroup style={{width: '100%', marginBottom: 15}}>

<Button

style={{width: '50%'}}

className="btn btn-sm"

bsStyle={!this.state.byRating ? "info": "default"}

onClick={() => this.changeByRatingVal(false)}>

По новизне

</Button>

<Button

style={{width: '50%'}}

className="btn btn-sm"

bsStyle={this.state.byRating ? "info": "default"}

onClick={() => this.changeByRatingVal(true)}>

По рейтингу

</Button>

</ButtonGroup>

<ListGroup className='search-list' style={{marginBottom: 0}}>

{this.props.genres && this.props.genres.map((item, pos) =>

<ListGroupItem

style={{height: 50, backgroundColor: pos % 2 === 0 ? "#fcfcfc": "#ffffff"}}

key={item.id} onClick={(e) => {

this.unselectGenre(item.id);

e.target.blur();

}}

>

<div style={{display: "flex"}}>

<div style={{minWidth: "50px"}}>

<Glyphicon style={{

color: this.state.unselectedGenres.includes(item.id) ? "#d5d5d5": "#5bc0de",

fontSize: "1.8em"

}} glyph="ok"/>

</div>

<h5>{item.name}</h5>

</div>

</ListGroupItem>

)}

</ListGroup>

</div>

</div>

</div>

);

}

}

Файл: Discasst\src\main\resources\static\app\components\statistics\StatisticsPage.js

import React, {Component} from 'react';

import StatisticsPanel from './StatisticsPanel';

import Navbar from "../Navbar";

export default class StatisticsPage extends Component {

constructor() {

super();

this.state = {

usersPerDay: '...',

usersPerMonth: '...',

podcastPerDay: '...',

podcastPerMonth: '...',

episodePerDay: '...',

episodePerMonth: '...',

}

}

componentDidMount() {

this.loadData();

}

loadData() {

fetch(`/getEpisodesCountToday`)

.then((response) => {

return response.json();

})

.then((json) => {

this.setState({episodePerDay: json});

});

fetch(`/getEpisodesCountThisMounth`)

.then((response) => {

return response.json();

})

.then((json) => {

this.setState({episodePerMonth: json});

});

fetch(`/getPodcastsCountToday`)

.then((response) => {

return response.json();

})

.then((json) => {

this.setState({podcastPerDay: json});

});

fetch(`/getPodcastsCountThisMounth`)

.then((response) => {

return response.json();

})

.then((json) => {

this.setState({podcastPerMonth: json});

});

fetch(`/getUsersCountToday`)

.then((response) => {

return response.json();

})

.then((json) => {

this.setState({usersPerDay: json});

});

fetch(`/getUsersCountThisMounth`)

.then((response) => {

return response.json();

})

.then((json) => {

this.setState({usersPerMonth: json});

});

}

render() {

const ts = this.state;

return (

<div>

<Navbar history={this.props.history}/>

<div>

<div style={{display: "flex", justifyContent: "center", flexWrap: "wrap"}}>

<StatisticsPanel time={statTime.MONTH} stat={stats.USER} number={ts.usersPerMonth}/>

<StatisticsPanel time={statTime.MONTH} stat={stats.EPISODE} number={ts.episodePerMonth}/>

<StatisticsPanel time={statTime.MONTH} stat={stats.PODCAST} number={ts.podcastPerMonth}/>

</div>

<div style={{display: "flex", justifyContent: "center", flexWrap: "wrap"}}>

<StatisticsPanel time={statTime.DAY} stat={stats.USER} number={ts.usersPerDay}/>

<StatisticsPanel time={statTime.DAY} stat={stats.EPISODE} number={ts.episodePerDay}/>

<StatisticsPanel time={statTime.DAY} stat={stats.PODCAST} number={ts.podcastPerDay}/>

</div>

</div>

</div>

);

}

}

const statTime = {

MONTH: 0,

DAY: 1

};

const stats = {

USER: 0,

PODCAST: 1,

EPISODE: 2,

};

export {stats, statTime};

Файл: Discasst\src\main\resources\static\app\components\statistics\StatisticsPanel.js

import React, {Component} from 'react';

import {Panel} from 'react-bootstrap';

import {stats, statTime} from './StatisticsPage';

export default class StatisticsPanel extends Component {

constructor() {

super();

this.state = {}

}

getStat() {

if (this.props.stat === stats.USER) return "Новых пользователей:";

if (this.props.stat === stats.PODCAST) return "Подкастов создано:";

if (this.props.stat === stats.EPISODE) return "Выпусков загружено:";

}

getTime() {

if (this.props.time === statTime.MONTH) return "ЗА МЕСЯЦ";

if (this.props.time === statTime.DAY) return "СЕГОДНЯ";

}

getLabelClass() {

if (this.props.time === statTime.MONTH) return "label label-info";

if (this.props.time === statTime.DAY) return "label label-success";

}

render() {

return (

<Panel style={{width: 320, margin: 10}}>

<Panel.Body>

<div className="text-center" style={{display: "flex", justifyContent: "center", flexWrap: "wrap"}}>

<h3>

<div className={this.getLabelClass()}>{this.getTime()}</div>

</h3>

<h3>{this.getStat()}</h3>

<Panel style={{width: "50%", backgroundColor: "#f1f1f1"}}>

<Panel.Body>

<h1 style={{margin: 0}}><strong><u>{this.props.number}</u></strong></h1>

</Panel.Body>

</Panel>

</div>

</Panel.Body>

</Panel>

);

}

}

Файл: Discasst\src\main\resources\static\app\components\userPage\AddCoauthorsPage.js

import React, {Component} from 'react';

import Navbar from "../Navbar";

import CoauthorsList from "./CoauthorsList";

import {getCookie, getFullCSRFParam} from "../Helper";

export default class AddCoauthorsPage extends Component {

constructor() {

super();

this.state = {

coauthors: []

}

}

componentDidMount() {

this.loadCoauthors();

}

loadCoauthors() {

fetch(`/getCoauthors?podcastId=${this.props.match.params.podcastId}`)

.then((response) => {

return response.json()

})

.then((json) => {

console.log(json)

let coauthorsIds = json.map(x => x.userId.toString());

this.setState({coauthors: coauthorsIds});

});

}

isId(x) {

return (x >= 0 && x % 1 === 0 && x.indexOf('.') === -1 && x.length <= 10 && x !== "")

}

addCoauthor = () => {

let tmp = this.state.coauthors;

let cid = this.refs.coauthorInput.value;

if (cid !== getCookie('userId'))

if (tmp.length <= 3 && this.isId(cid)) {

let newCoauthor = cid;

console.log(tmp.indexOf(newCoauthor));

if (tmp.indexOf(newCoauthor) < 0) {

this.refs.coauthorInput.value = "";

tmp = [...tmp, newCoauthor];

this.setState({coauthors: tmp});

}

}

};

deleteCoauthor = (item) => {

let tmp = this.state.coauthors;

let index = tmp.indexOf(item);

tmp.splice(index, 1);

this.setState({coauthors: tmp});

};

saveChanges = () => {

let authorsIds = JSON.stringify(this.state.coauthors);

const that = this;

$.ajax({

url: `/addCoauthors?podcastId=${this.props.match.params.podcastId}&${getFullCSRFParam()}`,

type: 'post',

contentType: 'application/json',

data: authorsIds,

success: () => {

that.changesIsApplyed();

},

error: (jqXhr, textStatus, errorThrown) => {

console.log(errorThrown + " " + textStatus);

}

});

};

changesIsApplyed = () => {

this.props.history.push(`/user/${getCookie("username")}`);

};

render() {

return (

<div>

<Navbar history={this.props.history}/>

<div style={{display: "flex", justifyContent: "center"}}>

<div className="container-fluid" style={{width: "50%"}}>

<br/><br/><br/>

<div className="panel panel-default">

<div className="panel-body" style={{maxWidth: '100%'}}>

{this.state.coauthors.length <= 3 ?

<div style={{display: "flex", justifyContent: "center"}}>

<input ref="coauthorInput" maxLength="10" className="form-control"

placeholder="Id соведущего" type="number" min="0" step="1"

max="999999999"/>&#160;&#160;

<button type="button" className="btn btn-info"

onClick={this.addCoauthor}>Добавить ведущего

</button>

</div>: <h4>Соведущие:</h4>}

{this.state.coauthors.length > 0 && <br/>}

<CoauthorsList coauthors={this.state.coauthors} onClick={this.deleteCoauthor}/>

<br/>

<button

type="button"

className="btn btn-info"

onClick={this.saveChanges}>Сохранить

</button>

<br/>

</div>

</div>

</div>

</div>

</div>

);

}

}

Файл: Discasst\src\main\resources\static\app\components\userPage\CoauthorsList.js

import React, {Component} from 'react';

export default class CoauthorsList extends Component {

constructor() {

super();

this.state = {}

}

render() {

return (<div className="container-fluid">

<div style={{display: "flex", justifyContent: "flex-start", flexWrap: "wrap"}}>

{this.props.coauthors.map((item) =>

<button key={item} onClick={() => this.props.onClick(item)} style={{margin: "2px"}}

className="btn btn-info">id: {item} <span className="glyphicon glyphicon-remove"></span>

</button>

)}

</div>

</div>

);

}

}

Файл: Discasst\src\main\resources\static\app\components\userPage\ CreateNewEpisodePage.js

import React, {Component} from 'react';

import Navbar from "../Navbar";

import {getCookie, getFullCSRFParam} from '../Helper'

export default class CreateNewEpisodePage extends Component {

constructor() {

super();

this.state = {}

}

createNewEpisode = () => {

if (this.validationIsOk()) {

console.log("Create new episode")

let obj = {

"name": this.refs.episodeName.value,

"podcastId": this.props.match.params.podcastId,

"description": this.refs.episodeDesc.value,

"podsterLink": this.refs.podsterLink.value,

};

let episode = JSON.stringify(obj);

const that = this;

$.ajax({

url: `/createEpisode?${getFullCSRFParam()}`,

type: 'post',

contentType: 'application/json',

data: episode,

success: () => {

that.episodeIsCreated();

},

error: (jqXhr, textStatus, errorThrown) => {

console.log(errorThrown + " " + textStatus);

}

});

}

};

episodeIsCreated() {

console.log("Episode is created");

this.props.history.push(`/user/${getCookie("username")}`);

}

validationIsOk() {

let en = this.refs.episodeName.value;

let ed = this.refs.episodeDesc.value;

let pl = this.refs.podsterLink.value;

if (en !== "" && ed !== "" && pl !== "") return true;

else return false;

}

render() {

return (

<div>

<Navbar history={this.props.history}/>

<div style={{display: "flex", justifyContent: "center"}}>

<div className="container-fluid" style={{width: "50%"}}>

<br/><br/><br/>

<div className="panel panel-default">

<div className="panel-body" style={{maxWidth: '100%'}}>

<input ref="episodeName" type="text" maxLength="50" className="form-control"

placeholder="* Название"/><br/>

<textarea ref="episodeDesc" className="form-control" maxLength="350"

style={{resize: "vertical"}} rows="5"

placeholder="* Описание"></textarea><br/>

<input ref="podsterLink" type="text" maxLength="50" className="form-control"

placeholder="* Ссылка на выпуск в Podster.fm (формата https://podcast-name.podster.fm/42)"/><br/>

<br/>

<div>

<button type="button" className="btn btn-info pull-right"

onClick={this.createNewEpisode}><span

className="glyphicon glyphicon-plus"></span> Добавить новый выпуск

</button>

</div>

<br/>

</div>

</div>

</div>

</div>

</div>

);

}

}

Файл: Discasst\src\main\resources\static\app\components\userPage\ CreateNewPodcastPage.js

import React, {Component} from 'react';

import Navbar from "../Navbar";

import {getCookie, getFullCSRFParam} from "../Helper";

export default class CreateNewPodcastPage extends Component {

constructor() {

super();

this.state = {

genres: null,

selectedGenre: {

id: null,

name: "* Выберите"

},

};

}

componentDidMount() {

this.getGenres();

}

getGenres = () => {

let genres;

$.getJSON(`/getGenres`, (data) => {

console.log(data);

this.setState({genres: data});

return data;

});

};

selectGenre = (genre) => {

this.setState({selectedGenre: genre});

};

validationIsOk() {

let pn = this.refs.podcastName.value;

let pd = this.refs.podcastDesc.value;

let g = this.state.selectedGenre.name;

if (pn !== "" && pd !== "" && g !== "* Выберите") return true;

return false;

}

createNewPodcast = () => {

if (this.validationIsOk()) {

console.log("Create new podcast");

let obj = {

"name": this.refs.podcastName.value,

"authorId": getCookie("userId"),

"description": this.refs.podcastDesc.value,

"genreId": (this.state.selectedGenre.id - 1),

"coauthors": this.state.coauthors,

"vkLink": this.refs.vkLink.value,

"twitterLink": this.refs.twitterLink.value,

"itunesLink": this.refs.itunesLink.value,

"facebookLink": this.refs.facebookLink.value,

"rating": 0

};

let podcast = JSON.stringify(obj);

const that = this;

$.ajax({

url: `/createPodcast?${getFullCSRFParam()}`,

type: 'post',

contentType: 'application/json',

data: podcast,

success: () => {

that.podcastIsCreated();

},

error: (jqXhr, textStatus, errorThrown) => {

console.log(errorThrown + " " + textStatus);

}

});

}

};

podcastIsCreated() {

console.log("Подкаст создан!")

this.props.history.push(`/user/${getCookie("username")}`);

}

render() {

return (

<div>

<Navbar history={this.props.history}/>

<div style={{display: "flex", justifyContent: "center"}}>

<div className="container-fluid" style={{width: "50%"}}>

<br/><br/><br/>

<div className="panel panel-default">

<div className="panel-body" style={{maxWidth: '100%'}}>

<input ref="podcastName" type="text" maxLength="50" className="form-control"

placeholder="* Название"/><br/>

<textarea ref="podcastDesc" className="form-control" maxLength="350"

style={{resize: "vertical"}} rows="5"

placeholder="* Описание"></textarea><br/>

<div className="btn-group">

<button className="btn btn-info dropdown-toggle" data-toggle="dropdown"

style={{minWidth: "160px"}}>{"Жанр: " + this.state.selectedGenre.name}&#160;&#160;

<span className="glyphicon glyphicon-chevron-down"></span></button>

{this.state.genres !== null && <ul className="dropdown-menu">

{this.state.genres.map((item) =>

<li key={item.id}><a role="button"

onClick={() => this.selectGenre(item)}>{item.name}</a>

</li>

)}

</ul>}

</div>

<br/><br/>

<input ref="twitterLink" type="text" maxLength="50" className="form-control"

placeholder="Ссылка на Twitter"/><br/>

<input ref="vkLink" type="text" maxLength="50" className="form-control"

placeholder="Ссылка на Vk"/><br/>

<input ref="facebookLink" type="text" maxLength="50" className="form-control"

placeholder="Ссылка на Facebook"/><br/>

<input ref="itunesLink" type="text" maxLength="50" className="form-control"

placeholder="Ссылка на iTunes"/><br/>

<div>

<button type="button" className="btn btn-info pull-right"

onClick={this.createNewPodcast}><span

className="glyphicon glyphicon-plus"></span> Создать новый подкаст

</but...


Подобные документы

  • Обзор существующих технологий разработки программного обеспечения. Описание платформы NET Framework. Принцип работы платформы: компиляция исходного кода; процесс загрузки и исполнения кода; IL-код и верификация. Новые возможности платформы NET Framework.

    реферат [30,7 K], добавлен 01.03.2011

  • Последовательность разработки информационного обеспечения очного и дистанционного обучения через просмотры и прослушивание подкастов. Создание веб-сайта или модуля существующей системы. Описание интерфейсов системы. Настройка прав доступа к подкастам.

    дипломная работа [2,3 M], добавлен 19.11.2010

  • Знакомство с особенностями и этапами разработки приложения для платформы Android. Рассмотрение функций персонажа: бег, прыжок, взаимодействие с объектами. Анализ блок-схемы алгоритма генерации платформ. Способы настройки функционала рабочей области.

    дипломная работа [3,4 M], добавлен 19.01.2017

  • Основы работы с многооконным графическим пользовательским интерфейсом операционной системы Windows95/NT. Основы работы с прикладными программами Windows и DOS. Разработка простого приложения для Windows при помощи средства разработки приложений DELPHI.

    контрольная работа [281,0 K], добавлен 15.01.2009

  • Облачные технологии - использование приложения, расположенного на удаленных серверах, посредством удобного пользовательского интерфейса. История и перспективы их развития. Платформы обслуживания "облаков". Положительные и отрицательные стороны сервисов.

    презентация [770,2 K], добавлен 14.03.2017

  • Отличительные черты смартфонов и коммуникаторов от обычных мобильных телефонов, их дополнительные возможности. Назначение и конфигурация платформы J2ME, ее функции. Порядок проектирования приложения для мобильного телефона на основе платформы J2ME.

    дипломная работа [3,6 M], добавлен 05.09.2009

  • Анализ хозяйственной деятельности организации и ее состояния. Особенности работы мобильной платформы. Реквизитный состав документов. Программная реализация и оценка эффективности приложения. Безопасность работы с приложением и безопасность данных.

    дипломная работа [1,0 M], добавлен 13.06.2014

  • Рассмотрение поисковых систем интернета как программно-аппаратного комплекса с веб-интерфейсом, предоставляющего возможность поиска информации. Виды поисковых систем: Archie, Wandex, Aliweb, WebCrawler, AltaVista, Yahoo!, Google, Яндекс, Bing и Rambler.

    реферат [24,3 K], добавлен 10.05.2013

  • База знаний интеллектуальной справочной системы по алгебре дробей со стандартными набором информационно-поисковых операций, пользовательским интерфейсом. Тестирование на стандартных вопросах и шаблонах поиска. Интеграция со смежными предметными областями.

    курсовая работа [12,3 M], добавлен 06.05.2011

  • Описание языков программирования Java и JavaFX. Среда разработки NetBeans и класс численных методов. Архитектура и принцип работы апплета с понятным пользовательским интерфейсом. Разработка алгоритма программы на примере модели межвидовой конкуренции.

    курсовая работа [1023,2 K], добавлен 19.09.2012

  • Понятие и функциональные особенности Java Card как версии Java-платформы для устройств с крайне ограниченными вычислительными ресурсами, оценка ее возможностей и необходимых ресурсов. Анализ степени безопасности платформы, взаимодействие компонентов.

    презентация [1,0 M], добавлен 19.05.2014

  • Описание платформы Deductor, ее назначение. Организационная структура аналитической платформы Deductor, состав модулей. Принципы работы программы, импорт и экспорт данных. Визуализация информации, сценарная последовательность и мастер обработки.

    курсовая работа [3,7 M], добавлен 19.04.2014

  • Рассмотрение и анализ моделей и алгоритмов семантического поиска в мультиагентной системе поддержки пользователей. Ознакомление с интерфейсом чата с ботом. Изучение и характеристика экспериментальных оценок релевантности и пертинентности запросов.

    дипломная работа [3,0 M], добавлен 13.10.2017

  • Изучение информационной базы клиента "Управление торговлей". Выбор и изучение платформы для построения сайта. Выбор технологии и среды разработки. Разработка основных алгоритмов решения задач и хранения данных. Проектирование интерфейса пользователя.

    дипломная работа [1,1 M], добавлен 20.05.2017

  • Разработка базы данных средней сложности с типовым пользовательским интерфейсом, а в частности, разработка базы данных СНАБЖЕНИЕ МАГАЗИНОВ на основе реляционной системы управления базами данных Microsoft Access, входящей в комплект Microsoft Office.

    курсовая работа [2,1 M], добавлен 02.12.2012

  • Разработка компьютерного приложения "Кипящая жидкость" с применением навыков программирования на языке Java. Проектирование алгоритма для решения поставленной задачи, его предметная область. Создание приложения с графическим пользовательским интерфейсом.

    отчет по практике [3,0 M], добавлен 29.10.2015

  • Возможности среды программирования delphi при разработке приложения с визуальным интерфейсом. Разработка спецификации программного обеспечения и на ее основе кода программного продукта. Отладка программы "трассировкой", ее тестирование и оптимизация.

    курсовая работа [501,4 K], добавлен 07.12.2016

  • Возможность поиска информации в режиме продвинутого диалога на естественном языке. Системы с интеллектуальным интерфейсом. Экспертные, самообучающиеся и адаптивные системы. Интеллектуальные базы данных. Системы контекстной и когнитивной помощи.

    презентация [224,2 K], добавлен 16.10.2013

  • Средства поиска информации в сети Интернет. Основные требования и методика поиска информации. Структура и характеристика поисковых сервисов. Глобальные поисковые машины WWW (World Wide Web). Планирование поиска и сбора информации в сети Интернет.

    реферат [32,2 K], добавлен 02.11.2010

  • Создание информационной системы работы такси с целью обеспечения диспетчерам более быстрого и удобного поиска необходимой информации. Создание таблиц и связей для работы с базами данных в среде Microsoft Access 2007. разработка запросов и отчетов.

    курсовая работа [3,1 M], добавлен 06.05.2013

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.