Folder structure #398

Merged
bones7242 merged 76 commits from folder-structure into master 2018-03-20 00:01:08 +01:00
177 changed files with 11432 additions and 430 deletions

View file

@ -1,3 +1,4 @@
node_modules/ node_modules/
public/ public/bundle
index.js
test test

19
.gitignore vendored
View file

@ -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

View file

@ -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'),
} }

View file

@ -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)

View file

@ -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);
}; };

View file

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

View file

@ -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);
} }

View file

@ -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 (

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

View file

@ -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 (

View file

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

View file

@ -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;

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

View file

@ -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}

View file

@ -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}

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

View file

@ -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>

View file

@ -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,
}; };
}; };

View file

@ -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>

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

View 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;

View 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;

View file

@ -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';

View file

@ -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';

View file

@ -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';

View file

@ -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,8 +26,6 @@ const initialState = {
license : '', license : '',
nsfw : false, nsfw : false,
}, },
thumbnailChannel : publish.thumbnailChannel,
thumbnailChannelId: publish.thumbnailChannelId,
thumbnail: null, thumbnail: null,
}; };

34
client/reducers/site.js Normal file
View 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;
}
}

View file

@ -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));
}; };

View file

@ -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));
} }

View file

@ -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
View file

@ -0,0 +1,7 @@
export const selectSiteState = (state) => {
return state.site;
};
export const selectSiteHost = (state) => {
return state.site.host;
};

View 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