Folder structure #398
177 changed files with 11432 additions and 430 deletions
.eslintignore.gitignore.sequelizercREADME.md
client
actions
api
app.jsxchannels
client.jscomponents
ActiveStatusBar
AssetPreview
ExpandingTextArea
GAListener
InactiveStatusBar
Logo
NavBarChannelOptionsDropdown
ProgressBar
PublishPreview
PublishUrlMiddleDisplay
SEO
constants
asset_display_states.jschannel_action_types.jspublish_action_types.jspublish_channel_select_states.jspublish_claim_states.jsshow_action_types.jsshow_request_types.js
containers
AssetDisplay
AssetInfo
AssetTitle
ChannelClaimsDisplay
ChannelCreateForm
ChannelLoginForm
ChannelSelect
Dropzone
FourOhFourPage
NavBar
PublishDetails
PublishDisabledMessage
PublishMetadataInputs
PublishStatus
PublishThumbnailInput
PublishTitleInput
PublishTool
PublishUrlInput
ShowAssetDetails
ShowAssetLite
ShowChannel
pages
AboutPage
ErrorPage
HomePage
LoginPage
ShowPage
reducers
sagas
selectors
utils
|
@ -1,3 +1,4 @@
|
||||||
node_modules/
|
node_modules/
|
||||||
public/
|
public/bundle
|
||||||
|
index.js
|
||||||
test
|
test
|
||||||
|
|
19
.gitignore
vendored
19
.gitignore
vendored
|
@ -1,7 +1,12 @@
|
||||||
node_modules
|
node_modules/
|
||||||
.idea
|
.idea/
|
||||||
config/sequelizeCliConfig.js
|
config/lbryConfig.js
|
||||||
config/speechConfig.js
|
config/loggerConfig.js
|
||||||
public/bundle
|
config/mysqlConfig.js
|
||||||
server.js
|
config/siteConfig.js
|
||||||
webpack.config.js
|
devConfig/slackConfig.js
|
||||||
|
devConfig/sequelizeCliConfig.js
|
||||||
|
devConfig/testingConfig.js
|
||||||
|
|
||||||
|
public/bundle/
|
||||||
|
index.js
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
'config': path.resolve('config', 'sequelizeCliConfig.js'),
|
'config': path.resolve('devConfig', 'sequelizeCliConfig.js'),
|
||||||
}
|
}
|
15
README.md
15
README.md
|
@ -1,7 +1,7 @@
|
||||||
# Spee.ch
|
# Spee.ch
|
||||||
Spee.ch is a web app that reads and publishes images and videos to and from the [LBRY](https://lbry.io/) blockchain.
|
Spee.ch is a web app that reads and publishes images and videos to and from the [LBRY](https://lbry.io/) blockchain.
|
||||||
|
|
||||||
## How to run this repository locally
|
##Installation
|
||||||
* start mysql
|
* start mysql
|
||||||
* install mysql
|
* install mysql
|
||||||
* create a database called `lbry`
|
* create a database called `lbry`
|
||||||
|
@ -12,10 +12,13 @@ Spee.ch is a web app that reads and publishes images and videos to and from the
|
||||||
* start spee.ch
|
* start spee.ch
|
||||||
* clone this repo
|
* clone this repo
|
||||||
* run `npm install`
|
* run `npm install`
|
||||||
* create your `speechConfig.js` file
|
* create your own config files in `/config`
|
||||||
* copy `speechConfig.js.example` and name it `speechConfig.js`
|
* copy `example.js.example` and name it `example.js`
|
||||||
* replace the `null` values in the config file with the appropriate values for your environment
|
* replace the `null` values in the config file with the appropriate values for your environment
|
||||||
* build the app by running `npm run build-prod`
|
* create your own config files in `/devConfig`
|
||||||
|
* copy `example.js.example` and name it `example.js`
|
||||||
|
* note: you must create these files, but the default values are sufficient if you do not want to update them.
|
||||||
|
* build the app by running `npm run build`
|
||||||
* to start the server, run `npm run start`
|
* to start the server, run `npm run start`
|
||||||
* visit [localhost:3000](http://localhost:3000)
|
* visit [localhost:3000](http://localhost:3000)
|
||||||
* start spee.ch-sync (optional, recommended)
|
* start spee.ch-sync (optional, recommended)
|
||||||
|
@ -45,8 +48,8 @@ Spee.ch is a web app that reads and publishes images and videos to and from the
|
||||||
* /api/claim/publish
|
* /api/claim/publish
|
||||||
* example: `curl -F 'name=MyPictureName' -F 'file=@/path/to/myPicture.jpeg' https://spee.ch/api/claim/publish`
|
* example: `curl -F 'name=MyPictureName' -F 'file=@/path/to/myPicture.jpeg' https://spee.ch/api/claim/publish`
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* `name`
|
* `name` (required)
|
||||||
* `file` (must be type .mp4, .jpeg, .jpg, .gif, or .png)
|
* `file` (required) (must be type .mp4, .jpeg, .jpg, .gif, or .png)
|
||||||
* `nsfw` (optional)
|
* `nsfw` (optional)
|
||||||
* `license` (optional)
|
* `license` (optional)
|
||||||
* `title` (optional)
|
* `title` (optional)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import Request from 'utils/request';
|
import Request from 'utils/request';
|
||||||
const { site: { host } } = require('../../config/speechConfig.js');
|
|
||||||
|
|
||||||
export function getLongClaimId (name, modifier) {
|
export function getLongClaimId (host, name, modifier) {
|
||||||
let body = {};
|
let body = {};
|
||||||
// create request params
|
// create request params
|
||||||
if (modifier) {
|
if (modifier) {
|
||||||
|
@ -24,12 +23,12 @@ export function getLongClaimId (name, modifier) {
|
||||||
return Request(url, params);
|
return Request(url, params);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getShortId (name, claimId) {
|
export function getShortId (host, name, claimId) {
|
||||||
const url = `${host}/api/claim/short-id/${claimId}/${name}`;
|
const url = `${host}/api/claim/short-id/${claimId}/${name}`;
|
||||||
return Request(url);
|
return Request(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getClaimData (name, claimId) {
|
export function getClaimData (host, name, claimId) {
|
||||||
const url = `${host}/api/claim/data/${name}/${claimId}`;
|
const url = `${host}/api/claim/data/${name}/${claimId}`;
|
||||||
return Request(url);
|
return Request(url);
|
||||||
};
|
};
|
|
@ -1,13 +1,12 @@
|
||||||
import Request from 'utils/request';
|
import Request from 'utils/request';
|
||||||
const { site: { host } } = require('../../config/speechConfig.js');
|
|
||||||
|
|
||||||
export function getChannelData (name, id) {
|
export function getChannelData (host, id, name) {
|
||||||
if (!id) id = 'none';
|
if (!id) id = 'none';
|
||||||
const url = `${host}/api/channel/data/${name}/${id}`;
|
const url = `${host}/api/channel/data/${name}/${id}`;
|
||||||
return Request(url);
|
return Request(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getChannelClaims (name, longId, page) {
|
export function getChannelClaims (host, longId, name, page) {
|
||||||
if (!page) page = 1;
|
if (!page) page = 1;
|
||||||
const url = `${host}/api/channel/claims/${name}/${longId}/${page}`;
|
const url = `${host}/api/channel/claims/${name}/${longId}/${page}`;
|
||||||
return Request(url);
|
return Request(url);
|
|
@ -1,12 +1,11 @@
|
||||||
import Request from 'utils/request';
|
import Request from 'utils/request';
|
||||||
const { site: { host } } = require('../../config/speechConfig.js');
|
|
||||||
|
|
||||||
export function checkFileAvailability (name, claimId) {
|
export function checkFileAvailability (claimId, host, name) {
|
||||||
const url = `${host}/api/file/availability/${name}/${claimId}`;
|
const url = `${host}/api/file/availability/${name}/${claimId}`;
|
||||||
return Request(url);
|
return Request(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function triggerClaimGet (name, claimId) {
|
export function triggerClaimGet (claimId, host, name) {
|
||||||
const url = `${host}/api/claim/get/${name}/${claimId}`;
|
const url = `${host}/api/claim/get/${name}/${claimId}`;
|
||||||
return Request(url);
|
return Request(url);
|
||||||
}
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Route, Switch } from 'react-router-dom';
|
import { Route, Switch } from 'react-router-dom';
|
||||||
import HomePage from 'components/HomePage';
|
import HomePage from 'pages/HomePage'; // or use the provided local homepage
|
||||||
import AboutPage from 'components/AboutPage';
|
import AboutPage from 'pages/AboutPage';
|
||||||
import LoginPage from 'containers/LoginPage';
|
import LoginPage from 'pages/LoginPage';
|
||||||
import ShowPage from 'containers/ShowPage';
|
import ShowPage from 'pages/ShowPage';
|
||||||
import FourOhFourPage from 'components/FourOhFourPage';
|
import FourOhFourPage from 'containers/FourOhFourPage';
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
return (
|
return (
|
10
client/components/AssetPreview/index.js
Normal file
10
client/components/AssetPreview/index.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import View from './view';
|
||||||
|
|
||||||
|
const mapStateToProps = ({site: {defaults: { defaultThumbnail }}}) => {
|
||||||
|
return {
|
||||||
|
defaultThumbnail,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, null)(View);
|
|
@ -1,8 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
const { claim: { defaultThumbnail } } = require('../../../config/speechConfig.js');
|
|
||||||
|
|
||||||
const AssetPreview = ({ claimData: { name, claimId, fileExt, contentType, thumbnail } }) => {
|
const AssetPreview = ({ defaultThumbnail, claimData: { name, claimId, fileExt, contentType, thumbnail } }) => {
|
||||||
const directSourceLink = `${claimId}/${name}.${fileExt}`;
|
const directSourceLink = `${claimId}/${name}.${fileExt}`;
|
||||||
const showUrlLink = `/${claimId}/${name}`;
|
const showUrlLink = `/${claimId}/${name}`;
|
||||||
return (
|
return (
|
|
@ -1,10 +1,9 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import GoogleAnalytics from 'react-ga';
|
import GoogleAnalytics from 'react-ga';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
const config = require('../../../config/speechConfig.js');
|
const { analytics: { googleId } } = require('../../../config/siteConfig.js');
|
||||||
const googleApiKey = config.analytics.googleId;
|
|
||||||
|
|
||||||
GoogleAnalytics.initialize(googleApiKey);
|
GoogleAnalytics.initialize(googleId);
|
||||||
|
|
||||||
class GAListener extends React.Component {
|
class GAListener extends React.Component {
|
||||||
componentDidMount () {
|
componentDidMount () {
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
class Preview extends React.Component {
|
class PublishPreview extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -53,10 +53,10 @@ class Preview extends React.Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Preview.propTypes = {
|
PublishPreview.propTypes = {
|
||||||
dimPreview: PropTypes.bool.isRequired,
|
dimPreview: PropTypes.bool.isRequired,
|
||||||
file : PropTypes.object.isRequired,
|
file : PropTypes.object.isRequired,
|
||||||
thumbnail : PropTypes.object,
|
thumbnail : PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Preview;
|
export default PublishPreview;
|
16
client/components/SEO/index.js
Normal file
16
client/components/SEO/index.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import View from './view';
|
||||||
|
|
||||||
|
const mapStateToProps = ({ site }) => {
|
||||||
|
const { defaultDescription, defaultThumbnail, description: siteDescription, host: siteHost, title: siteTitle, twitter: siteTwitter } = site;
|
||||||
|
return {
|
||||||
|
defaultDescription,
|
||||||
|
defaultThumbnail,
|
||||||
|
siteDescription,
|
||||||
|
siteHost,
|
||||||
|
siteTitle,
|
||||||
|
siteTwitter,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, null)(View);
|
|
@ -8,10 +8,16 @@ import { createCanonicalLink } from 'utils/canonicalLink';
|
||||||
|
|
||||||
class SEO extends React.Component {
|
class SEO extends React.Component {
|
||||||
render () {
|
render () {
|
||||||
let { pageTitle, asset, channel, pageUri } = this.props;
|
// props from state
|
||||||
pageTitle = createPageTitle(pageTitle);
|
const { defaultDescription, defaultThumbnail, siteDescription, siteHost, siteTitle, siteTwitter } = this.props;
|
||||||
const metaTags = createMetaTags(asset, channel);
|
// props from parent
|
||||||
const canonicalLink = createCanonicalLink(asset, channel, pageUri);
|
const { asset, channel, pageUri } = this.props;
|
||||||
|
let { pageTitle } = this.props;
|
||||||
|
// create page title, tags, and canonical link
|
||||||
|
pageTitle = createPageTitle(siteTitle, pageTitle);
|
||||||
|
const metaTags = createMetaTags(siteDescription, siteHost, siteTitle, siteTwitter, asset, channel, defaultDescription, defaultThumbnail);
|
||||||
|
const canonicalLink = createCanonicalLink(asset, channel, pageUri, siteHost);
|
||||||
|
// render results
|
||||||
return (
|
return (
|
||||||
<Helmet
|
<Helmet
|
||||||
title={pageTitle}
|
title={pageTitle}
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { validateFile } from 'utils/file';
|
import { validateFile } from 'utils/file';
|
||||||
import Preview from 'components/Preview';
|
import PublishPreview from 'components/PublishPreview';
|
||||||
|
|
||||||
class Dropzone extends React.Component {
|
class Dropzone extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
|
@ -87,7 +87,7 @@ class Dropzone extends React.Component {
|
||||||
<div id='preview-dropzone' className={'row row--padded row--tall dropzone' + (this.state.dragOver ? ' dropzone--drag-over' : '')} onDrop={this.handleDrop} onDragOver={this.handleDragOver} onDragEnd={this.handleDragEnd} onDragEnter={this.handleDragEnter} onDragLeave={this.handleDragLeave} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} onClick={this.handleClick}>
|
<div id='preview-dropzone' className={'row row--padded row--tall dropzone' + (this.state.dragOver ? ' dropzone--drag-over' : '')} onDrop={this.handleDrop} onDragOver={this.handleDragOver} onDragEnd={this.handleDragEnd} onDragEnter={this.handleDragEnter} onDragLeave={this.handleDragLeave} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} onClick={this.handleClick}>
|
||||||
{this.props.file ? (
|
{this.props.file ? (
|
||||||
<div>
|
<div>
|
||||||
<Preview
|
<PublishPreview
|
||||||
dimPreview={this.state.dimPreview}
|
dimPreview={this.state.dimPreview}
|
||||||
file={this.props.file}
|
file={this.props.file}
|
||||||
thumbnail={this.props.thumbnail}
|
thumbnail={this.props.thumbnail}
|
11
client/containers/FourOhFourPage/index.jsx
Normal file
11
client/containers/FourOhFourPage/index.jsx
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import View from './view';
|
||||||
|
|
||||||
|
const mapStateToProps = ({ site: { host, title } }) => {
|
||||||
|
return {
|
||||||
|
host,
|
||||||
|
title,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, null)(View);
|
|
@ -1,10 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import NavBar from 'containers/NavBar';
|
import NavBar from 'containers/NavBar';
|
||||||
import Helmet from 'react-helmet';
|
import Helmet from 'react-helmet';
|
||||||
const { site: { title, host } } = require('../../../config/speechConfig.js');
|
|
||||||
|
|
||||||
class FourOhForPage extends React.Component {
|
class FourOhForPage extends React.Component {
|
||||||
render () {
|
render () {
|
||||||
|
const {title, host} = this.props;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Helmet>
|
<Helmet>
|
|
@ -1,13 +1,14 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { updateLoggedInChannel } from 'actions/channel';
|
import { updateLoggedInChannel } from 'actions/channel';
|
||||||
|
import {updateSelectedChannel} from 'actions/publish';
|
||||||
import View from './view';
|
import View from './view';
|
||||||
import {updateSelectedChannel} from '../../actions/publish';
|
|
||||||
|
|
||||||
const mapStateToProps = ({ channel }) => {
|
const mapStateToProps = ({ channel, site }) => {
|
||||||
return {
|
return {
|
||||||
channelName : channel.loggedInChannel.name,
|
channelName : channel.loggedInChannel.name,
|
||||||
channelShortId: channel.loggedInChannel.shortId,
|
channelShortId: channel.loggedInChannel.shortId,
|
||||||
channelLongId : channel.loggedInChannel.longId,
|
channelLongId : channel.loggedInChannel.longId,
|
||||||
|
siteDescription: site.description,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -53,12 +53,13 @@ class NavBar extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
render () {
|
render () {
|
||||||
|
const { siteDescription } = this.props;
|
||||||
return (
|
return (
|
||||||
<div className='row row--wide nav-bar'>
|
<div className='row row--wide nav-bar'>
|
||||||
<div className='row row--padded row--short flex-container--row flex-container--space-between-center'>
|
<div className='row row--padded row--short flex-container--row flex-container--space-between-center'>
|
||||||
<Logo />
|
<Logo />
|
||||||
<div className='nav-bar--center'>
|
<div className='nav-bar--center'>
|
||||||
<span className='nav-bar-tagline'>Open-source, decentralized image and video sharing.</span>
|
<span className='nav-bar-tagline'>{siteDescription}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className='nav-bar--right'>
|
<div className='nav-bar--right'>
|
||||||
<NavLink className='nav-bar-link link--nav' activeClassName='link--nav-active' to='/' exact>Publish</NavLink>
|
<NavLink className='nav-bar-link link--nav' activeClassName='link--nav-active' to='/' exact>Publish</NavLink>
|
10
client/containers/PublishDisabledMessage/index.js
Normal file
10
client/containers/PublishDisabledMessage/index.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import {connect} from 'react-redux';
|
||||||
|
import View from './view';
|
||||||
|
|
||||||
|
const mapStateToProps = ({ publish }) => {
|
||||||
|
return {
|
||||||
|
message: publish.disabledMessage,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, null)(View);
|
16
client/containers/PublishDisabledMessage/view.jsx
Normal file
16
client/containers/PublishDisabledMessage/view.jsx
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
class PublishDisabledMessage extends React.Component {
|
||||||
|
render () {
|
||||||
|
const message = this.props.message;
|
||||||
|
console.log('this.props.message:', message);
|
||||||
|
return (
|
||||||
|
<div className='row dropzone--disabled row--tall flex-container--column flex-container--center-center'>
|
||||||
|
<p className='text--disabled'>Publishing is currently disabled.</p>
|
||||||
|
<p className='text--disabled'>{message}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PublishDisabledMessage;
|
30
client/containers/PublishTool/view.jsx
Normal file
30
client/containers/PublishTool/view.jsx
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import React from 'react';
|
||||||
|
import Dropzone from 'containers/Dropzone';
|
||||||
|
import PublishDetails from 'containers/PublishDetails';
|
||||||
|
import PublishStatus from 'containers/PublishStatus';
|
||||||
|
import PublishDisabledMessage from 'containers/PublishDisabledMessage';
|
||||||
|
|
||||||
|
class PublishTool extends React.Component {
|
||||||
|
render () {
|
||||||
|
if (this.props.disabled) {
|
||||||
|
console.log('publish is disabled');
|
||||||
|
return (
|
||||||
|
<PublishDisabledMessage />
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log('publish is not disabled');
|
||||||
|
if (this.props.file) {
|
||||||
|
if (this.props.status) {
|
||||||
|
return (
|
||||||
|
<PublishStatus />
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <PublishDetails />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return <Dropzone />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PublishTool;
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import SEO from 'components/SEO';
|
import SEO from 'components/SEO';
|
||||||
import NavBar from 'containers/NavBar';
|
import NavBar from 'containers/NavBar';
|
||||||
import ErrorPage from 'components/ErrorPage';
|
import ErrorPage from 'pages/ErrorPage';
|
||||||
import AssetTitle from 'containers/AssetTitle';
|
import AssetTitle from 'containers/AssetTitle';
|
||||||
import AssetDisplay from 'containers/AssetDisplay';
|
import AssetDisplay from 'containers/AssetDisplay';
|
||||||
import AssetInfo from 'containers/AssetInfo';
|
import AssetInfo from 'containers/AssetInfo';
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import SEO from 'components/SEO';
|
import SEO from 'components/SEO';
|
||||||
import ErrorPage from 'components/ErrorPage';
|
import ErrorPage from 'pages/ErrorPage';
|
||||||
import NavBar from 'containers/NavBar';
|
import NavBar from 'containers/NavBar';
|
||||||
import ChannelClaimsDisplay from 'containers/ChannelClaimsDisplay';
|
import ChannelClaimsDisplay from 'containers/ChannelClaimsDisplay';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ErrorPage from 'components/ErrorPage';
|
import ErrorPage from 'pages/ErrorPage';
|
||||||
import ShowAssetLite from 'containers/ShowAssetLite';
|
import ShowAssetLite from 'containers/ShowAssetLite';
|
||||||
import ShowAssetDetails from 'containers/ShowAssetDetails';
|
import ShowAssetDetails from 'containers/ShowAssetDetails';
|
||||||
import ShowChannel from 'containers/ShowChannel';
|
import ShowChannel from 'containers/ShowChannel';
|
|
@ -1,9 +1,10 @@
|
||||||
import * as actions from 'constants/publish_action_types';
|
import * as actions from 'constants/publish_action_types';
|
||||||
import { LOGIN } from 'constants/publish_channel_select_states';
|
import { LOGIN } from 'constants/publish_channel_select_states';
|
||||||
const { publish } = require('../../config/speechConfig.js');
|
const { publishing } = require('../../config/siteConfig.js');
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
disabled : publish.disabled,
|
disabled : publishing.disabled,
|
||||||
|
disabledMessage : publishing.disabledMessage,
|
||||||
publishInChannel : false,
|
publishInChannel : false,
|
||||||
selectedChannel : LOGIN,
|
selectedChannel : LOGIN,
|
||||||
showMetadataInputs: false,
|
showMetadataInputs: false,
|
||||||
|
@ -25,9 +26,7 @@ const initialState = {
|
||||||
license : '',
|
license : '',
|
||||||
nsfw : false,
|
nsfw : false,
|
||||||
},
|
},
|
||||||
thumbnailChannel : publish.thumbnailChannel,
|
thumbnail: null,
|
||||||
thumbnailChannelId: publish.thumbnailChannelId,
|
|
||||||
thumbnail : null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function (state = initialState, action) {
|
export default function (state = initialState, action) {
|
34
client/reducers/site.js
Normal file
34
client/reducers/site.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
const siteConfig = require('../../config/siteConfig.js');
|
||||||
|
|
||||||
|
const {
|
||||||
|
analytics: {
|
||||||
|
googleId: googleAnalyticsId,
|
||||||
|
},
|
||||||
|
assetDefaults: {
|
||||||
|
thumbnail: defaultThumbnail,
|
||||||
|
description: defaultDescription,
|
||||||
|
},
|
||||||
|
details: {
|
||||||
|
description,
|
||||||
|
host,
|
||||||
|
title,
|
||||||
|
twitter,
|
||||||
|
},
|
||||||
|
} = siteConfig;
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
description,
|
||||||
|
googleAnalyticsId,
|
||||||
|
host,
|
||||||
|
title,
|
||||||
|
twitter,
|
||||||
|
defaultDescription,
|
||||||
|
defaultThumbnail,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function (state = initialState, action) {
|
||||||
|
switch (action.type) {
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,18 @@
|
||||||
import { call, put, takeLatest } from 'redux-saga/effects';
|
import {call, put, select, takeLatest} from 'redux-saga/effects';
|
||||||
import * as actions from 'constants/show_action_types';
|
import * as actions from 'constants/show_action_types';
|
||||||
import { updateFileAvailability, updateDisplayAssetError } from 'actions/show';
|
import { updateFileAvailability, updateDisplayAssetError } from 'actions/show';
|
||||||
import { UNAVAILABLE, AVAILABLE } from 'constants/asset_display_states';
|
import { UNAVAILABLE, AVAILABLE } from 'constants/asset_display_states';
|
||||||
import { checkFileAvailability, triggerClaimGet } from 'api/fileApi';
|
import { checkFileAvailability, triggerClaimGet } from 'api/fileApi';
|
||||||
|
import { selectSiteHost } from 'selectors/site';
|
||||||
|
|
||||||
function * retrieveFile (action) {
|
function * retrieveFile (action) {
|
||||||
const name = action.data.name;
|
const name = action.data.name;
|
||||||
const claimId = action.data.claimId;
|
const claimId = action.data.claimId;
|
||||||
|
const host = yield select(selectSiteHost);
|
||||||
// see if the file is available
|
// see if the file is available
|
||||||
let isAvailable;
|
let isAvailable;
|
||||||
try {
|
try {
|
||||||
({ data: isAvailable } = yield call(checkFileAvailability, name, claimId));
|
({ data: isAvailable } = yield call(checkFileAvailability, claimId, host, name));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return yield put(updateDisplayAssetError(error.message));
|
return yield put(updateDisplayAssetError(error.message));
|
||||||
};
|
};
|
||||||
|
@ -21,7 +23,7 @@ function * retrieveFile (action) {
|
||||||
yield put(updateFileAvailability(UNAVAILABLE));
|
yield put(updateFileAvailability(UNAVAILABLE));
|
||||||
// initiate get request for the file
|
// initiate get request for the file
|
||||||
try {
|
try {
|
||||||
yield call(triggerClaimGet, name, claimId);
|
yield call(triggerClaimGet, claimId, host, name);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return yield put(updateDisplayAssetError(error.message));
|
return yield put(updateDisplayAssetError(error.message));
|
||||||
};
|
};
|
|
@ -3,6 +3,7 @@ import * as actions from 'constants/show_action_types';
|
||||||
import { addRequestToRequestList, onRequestError, onRequestUpdate, addAssetToAssetList } from 'actions/show';
|
import { addRequestToRequestList, onRequestError, onRequestUpdate, addAssetToAssetList } from 'actions/show';
|
||||||
import { getLongClaimId, getShortId, getClaimData } from 'api/assetApi';
|
import { getLongClaimId, getShortId, getClaimData } from 'api/assetApi';
|
||||||
import { selectShowState } from 'selectors/show';
|
import { selectShowState } from 'selectors/show';
|
||||||
|
import { selectSiteHost } from 'selectors/site';
|
||||||
|
|
||||||
export function * newAssetRequest (action) {
|
export function * newAssetRequest (action) {
|
||||||
const { requestType, requestId, name, modifier } = action.data;
|
const { requestType, requestId, name, modifier } = action.data;
|
||||||
|
@ -11,13 +12,14 @@ export function * newAssetRequest (action) {
|
||||||
// is this an existing request?
|
// is this an existing request?
|
||||||
// If this uri is in the request list, it's already been fetched
|
// If this uri is in the request list, it's already been fetched
|
||||||
const state = yield select(selectShowState);
|
const state = yield select(selectShowState);
|
||||||
|
const host = yield select(selectSiteHost);
|
||||||
if (state.requestList[requestId]) {
|
if (state.requestList[requestId]) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// get long id && add request to request list
|
// get long id && add request to request list
|
||||||
let longId;
|
let longId;
|
||||||
try {
|
try {
|
||||||
({data: longId} = yield call(getLongClaimId, name, modifier));
|
({data: longId} = yield call(getLongClaimId, host, name, modifier));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return yield put(onRequestError(error.message));
|
return yield put(onRequestError(error.message));
|
||||||
}
|
}
|
||||||
|
@ -31,14 +33,14 @@ export function * newAssetRequest (action) {
|
||||||
// get short Id
|
// get short Id
|
||||||
let shortId;
|
let shortId;
|
||||||
try {
|
try {
|
||||||
({data: shortId} = yield call(getShortId, name, longId));
|
({data: shortId} = yield call(getShortId, host, name, longId));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return yield put(onRequestError(error.message));
|
return yield put(onRequestError(error.message));
|
||||||
}
|
}
|
||||||
// get asset claim data
|
// get asset claim data
|
||||||
let claimData;
|
let claimData;
|
||||||
try {
|
try {
|
||||||
({data: claimData} = yield call(getClaimData, name, longId));
|
({data: claimData} = yield call(getClaimData, host, name, longId));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return yield put(onRequestError(error.message));
|
return yield put(onRequestError(error.message));
|
||||||
}
|
}
|
|
@ -3,6 +3,7 @@ import * as actions from 'constants/show_action_types';
|
||||||
import { addNewChannelToChannelList, addRequestToRequestList, onRequestError, onRequestUpdate, updateChannelClaims } from 'actions/show';
|
import { addNewChannelToChannelList, addRequestToRequestList, onRequestError, onRequestUpdate, updateChannelClaims } from 'actions/show';
|
||||||
import { getChannelClaims, getChannelData } from 'api/channelApi';
|
import { getChannelClaims, getChannelData } from 'api/channelApi';
|
||||||
import { selectShowState } from 'selectors/show';
|
import { selectShowState } from 'selectors/show';
|
||||||
|
import { selectSiteHost } from 'selectors/site';
|
||||||
|
|
||||||
export function * newChannelRequest (action) {
|
export function * newChannelRequest (action) {
|
||||||
const { requestType, requestId, channelName, channelId } = action.data;
|
const { requestType, requestId, channelName, channelId } = action.data;
|
||||||
|
@ -11,13 +12,14 @@ export function * newChannelRequest (action) {
|
||||||
// is this an existing request?
|
// is this an existing request?
|
||||||
// If this uri is in the request list, it's already been fetched
|
// If this uri is in the request list, it's already been fetched
|
||||||
const state = yield select(selectShowState);
|
const state = yield select(selectShowState);
|
||||||
|
const host = yield select(selectSiteHost);
|
||||||
if (state.requestList[requestId]) {
|
if (state.requestList[requestId]) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// get channel long id
|
// get channel long id
|
||||||
let longId, shortId;
|
let longId, shortId;
|
||||||
try {
|
try {
|
||||||
({ data: {longChannelClaimId: longId, shortChannelClaimId: shortId} } = yield call(getChannelData, channelName, channelId));
|
({ data: {longChannelClaimId: longId, shortChannelClaimId: shortId} } = yield call(getChannelData, host, channelName, channelId));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return yield put(onRequestError(error.message));
|
return yield put(onRequestError(error.message));
|
||||||
}
|
}
|
||||||
|
@ -32,7 +34,7 @@ export function * newChannelRequest (action) {
|
||||||
// get channel claims data
|
// get channel claims data
|
||||||
let claimsData;
|
let claimsData;
|
||||||
try {
|
try {
|
||||||
({ data: claimsData } = yield call(getChannelClaims, channelName, longId, 1));
|
({ data: claimsData } = yield call(getChannelClaims, host, longId, channelName, 1));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return yield put(onRequestError(error.message));
|
return yield put(onRequestError(error.message));
|
||||||
}
|
}
|
||||||
|
@ -48,9 +50,10 @@ export function * watchNewChannelRequest () {
|
||||||
|
|
||||||
function * getNewClaimsAndUpdateChannel (action) {
|
function * getNewClaimsAndUpdateChannel (action) {
|
||||||
const { channelKey, name, longId, page } = action.data;
|
const { channelKey, name, longId, page } = action.data;
|
||||||
|
const host = yield select(selectSiteHost);
|
||||||
let claimsData;
|
let claimsData;
|
||||||
try {
|
try {
|
||||||
({ data: claimsData } = yield call(getChannelClaims, name, longId, page));
|
({ data: claimsData } = yield call(getChannelClaims, host, longId, name, page));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return yield put(onRequestError(error.message));
|
return yield put(onRequestError(error.message));
|
||||||
}
|
}
|
7
client/selectors/site.js
Normal file
7
client/selectors/site.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
export const selectSiteState = (state) => {
|
||||||
|
return state.site;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const selectSiteHost = (state) => {
|
||||||
|
return state.site.host;
|
||||||
|
};
|
29
client/utils/canonicalLink.js
Normal file
29
client/utils/canonicalLink.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
const createBasicCanonicalLink = (page, siteHost) => {
|
||||||
|
return `${siteHost}/${page}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createAssetCanonicalLink = (asset, siteHost) => {
|
||||||
|
let channelName, certificateId, name, claimId;
|
||||||
|
if (asset.claimData) {
|
||||||
|
({ channelName, certificateId, name, claimId } = asset.claimData);
|
||||||
|
};
|
||||||
|
if (channelName) {
|
||||||
|
return `${siteHost}/${channelName}:${certificateId}/${name}`;
|
||||||
|
};
|
||||||
|
return `${siteHost}/${claimId}/${name}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createChannelCanonicalLink = (channel, siteHost) => {
|
||||||
|
const { name, longId } = channel;
|
||||||
|
return `${siteHost}/${name}:${longId}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createCanonicalLink = (asset, channel, page, siteHost) => {
|
||||||
|
if (asset) {
|
||||||
|
return createAssetCanonicalLink(asset, siteHost);
|
||||||
|
}
|
||||||
|
if (channel) {
|
||||||
|
return createChannelCanonicalLink(channel, siteHost);
|
||||||
|
}
|
||||||
|
return createBasicCanonicalLink(page, siteHost);
|
||||||
|
};
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue