mirror of
https://github.com/ascribe/onion.git
synced 2025-01-03 18:35:09 +01:00
Merge branch 'master' into AD-1342-Public-piece-note-doesnt-render-correctly-def-data-null
This commit is contained in:
commit
81f28a02f9
19
js/actions/webhook_actions.js
Normal file
19
js/actions/webhook_actions.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import { alt } from '../alt';
|
||||||
|
|
||||||
|
|
||||||
|
class WebhookActions {
|
||||||
|
constructor() {
|
||||||
|
this.generateActions(
|
||||||
|
'fetchWebhooks',
|
||||||
|
'successFetchWebhooks',
|
||||||
|
'fetchWebhookEvents',
|
||||||
|
'successFetchWebhookEvents',
|
||||||
|
'removeWebhook',
|
||||||
|
'successRemoveWebhook'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default alt.createActions(WebhookActions);
|
@ -41,7 +41,7 @@ let AclButtonList = React.createClass({
|
|||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
UserStore.listen(this.onChange);
|
UserStore.listen(this.onChange);
|
||||||
UserActions.fetchCurrentUser();
|
UserActions.fetchCurrentUser.defer();
|
||||||
|
|
||||||
window.addEventListener('resize', this.handleResize);
|
window.addEventListener('resize', this.handleResize);
|
||||||
window.dispatchEvent(new Event('resize'));
|
window.dispatchEvent(new Event('resize'));
|
||||||
|
@ -42,8 +42,7 @@ import { getLangText } from '../../utils/lang_utils';
|
|||||||
let Edition = React.createClass({
|
let Edition = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
edition: React.PropTypes.object,
|
edition: React.PropTypes.object,
|
||||||
loadEdition: React.PropTypes.func,
|
loadEdition: React.PropTypes.func
|
||||||
location: React.PropTypes.object
|
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [History],
|
mixins: [History],
|
||||||
@ -156,8 +155,7 @@ let Edition = React.createClass({
|
|||||||
pieceId={this.props.edition.parent}
|
pieceId={this.props.edition.parent}
|
||||||
extraData={this.props.edition.extra_data}
|
extraData={this.props.edition.extra_data}
|
||||||
otherData={this.props.edition.other_data}
|
otherData={this.props.edition.other_data}
|
||||||
handleSuccess={this.props.loadEdition}
|
handleSuccess={this.props.loadEdition} />
|
||||||
location={this.props.location}/>
|
|
||||||
</CollapsibleParagraph>
|
</CollapsibleParagraph>
|
||||||
<CollapsibleParagraph
|
<CollapsibleParagraph
|
||||||
title={getLangText('SPOOL Details')}>
|
title={getLangText('SPOOL Details')}>
|
||||||
|
@ -17,8 +17,7 @@ import { setDocumentTitle } from '../../utils/dom_utils';
|
|||||||
*/
|
*/
|
||||||
let EditionContainer = React.createClass({
|
let EditionContainer = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
params: React.PropTypes.object,
|
params: React.PropTypes.object
|
||||||
location: React.PropTypes.object
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
@ -67,14 +66,13 @@ let EditionContainer = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if(this.state.edition && this.state.edition.title) {
|
if(this.state.edition && this.state.edition.id) {
|
||||||
setDocumentTitle([this.state.edition.artist_name, this.state.edition.title].join(', '));
|
setDocumentTitle([this.state.edition.artist_name, this.state.edition.title].join(', '));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Edition
|
<Edition
|
||||||
edition={this.state.edition}
|
edition={this.state.edition}
|
||||||
loadEdition={this.loadEdition}
|
loadEdition={this.loadEdition} />
|
||||||
location={this.props.location}/>
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
|
@ -23,8 +23,7 @@ let FurtherDetails = React.createClass({
|
|||||||
pieceId: React.PropTypes.number,
|
pieceId: React.PropTypes.number,
|
||||||
extraData: React.PropTypes.object,
|
extraData: React.PropTypes.object,
|
||||||
otherData: React.PropTypes.arrayOf(React.PropTypes.object),
|
otherData: React.PropTypes.arrayOf(React.PropTypes.object),
|
||||||
handleSuccess: React.PropTypes.func,
|
handleSuccess: React.PropTypes.func
|
||||||
location: React.PropTypes.object
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
@ -86,8 +85,7 @@ let FurtherDetails = React.createClass({
|
|||||||
overrideForm={true}
|
overrideForm={true}
|
||||||
pieceId={this.props.pieceId}
|
pieceId={this.props.pieceId}
|
||||||
otherData={this.props.otherData}
|
otherData={this.props.otherData}
|
||||||
multiple={true}
|
multiple={true} />
|
||||||
location={this.props.location}/>
|
|
||||||
</Form>
|
</Form>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
@ -50,8 +50,7 @@ import { setDocumentTitle } from '../../utils/dom_utils';
|
|||||||
*/
|
*/
|
||||||
let PieceContainer = React.createClass({
|
let PieceContainer = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
params: React.PropTypes.object,
|
params: React.PropTypes.object
|
||||||
location: React.PropTypes.object
|
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [History],
|
mixins: [History],
|
||||||
@ -225,7 +224,7 @@ let PieceContainer = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if(this.state.piece && this.state.piece.title) {
|
if(this.state.piece && this.state.piece.id) {
|
||||||
setDocumentTitle([this.state.piece.artist_name, this.state.piece.title].join(', '));
|
setDocumentTitle([this.state.piece.artist_name, this.state.piece.title].join(', '));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -291,8 +290,7 @@ let PieceContainer = React.createClass({
|
|||||||
pieceId={this.state.piece.id}
|
pieceId={this.state.piece.id}
|
||||||
extraData={this.state.piece.extra_data}
|
extraData={this.state.piece.extra_data}
|
||||||
otherData={this.state.piece.other_data}
|
otherData={this.state.piece.other_data}
|
||||||
handleSuccess={this.loadPiece}
|
handleSuccess={this.loadPiece} />
|
||||||
location={this.props.location}/>
|
|
||||||
</CollapsibleParagraph>
|
</CollapsibleParagraph>
|
||||||
|
|
||||||
</Piece>
|
</Piece>
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { History } from 'react-router';
|
import { History, RouteContext } from 'react-router';
|
||||||
|
|
||||||
import UserStore from '../../../stores/user_store';
|
import UserStore from '../../../stores/user_store';
|
||||||
import UserActions from '../../../actions/user_actions';
|
import UserActions from '../../../actions/user_actions';
|
||||||
|
|
||||||
import AppConstants from '../../../constants/application_constants';
|
import AppConstants from '../../../constants/application_constants';
|
||||||
|
|
||||||
import { InjectInHeadUtils } from '../../../utils/inject_utils';
|
|
||||||
|
|
||||||
|
|
||||||
const { object } = React.PropTypes;
|
const { object } = React.PropTypes;
|
||||||
const WHEN_ENUM = ['loggedIn', 'loggedOut'];
|
const WHEN_ENUM = ['loggedIn', 'loggedOut'];
|
||||||
@ -39,7 +37,9 @@ export default function AuthProxyHandler({to, when}) {
|
|||||||
location: object
|
location: object
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [History],
|
// We need insert `RouteContext` here in order to be able
|
||||||
|
// to use the `Lifecycle` widget in further down nested components
|
||||||
|
mixins: [History, RouteContext],
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return UserStore.getState();
|
return UserStore.getState();
|
||||||
@ -55,7 +55,6 @@ export default function AuthProxyHandler({to, when}) {
|
|||||||
// data from the server
|
// data from the server
|
||||||
if(!UserStore.isLoading()) {
|
if(!UserStore.isLoading()) {
|
||||||
this.redirectConditionally();
|
this.redirectConditionally();
|
||||||
this.injectSpecialLoveMessage();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -63,23 +62,6 @@ export default function AuthProxyHandler({to, when}) {
|
|||||||
UserStore.unlisten(this.onChange);
|
UserStore.unlisten(this.onChange);
|
||||||
},
|
},
|
||||||
|
|
||||||
injectSpecialLoveMessage() {
|
|
||||||
const { currentUser } = this.state;
|
|
||||||
|
|
||||||
if(currentUser && (currentUser.email === 'dimi@mailinator.com'
|
|
||||||
|| currentUser.email === 'trent@ascribe.io'
|
|
||||||
|| currentUser.email === 'wojciech@ascribe.io'
|
|
||||||
|| currentUser.email === 'rod@mailinator.com'
|
|
||||||
|| currentUser.email === 'qisheng.brett.sun@gmail.com'
|
|
||||||
|| currentUser.email === 'sylvain@ascribe.io')) {
|
|
||||||
if(!InjectInHeadUtils.isPresent('script', AppConstants.fartscroll.sdkUrl)) {
|
|
||||||
InjectInHeadUtils.inject(AppConstants.fartscroll.sdkUrl).then(() => {
|
|
||||||
window.fartscroll ? window.fartscroll() : null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
redirectConditionally() {
|
redirectConditionally() {
|
||||||
const { query } = this.props.location;
|
const { query } = this.props.location;
|
||||||
const { redirectAuthenticated, redirect } = query;
|
const { redirectAuthenticated, redirect } = query;
|
||||||
|
@ -11,6 +11,7 @@ import WhitelabelActions from '../../actions/whitelabel_actions';
|
|||||||
import AccountSettings from './account_settings';
|
import AccountSettings from './account_settings';
|
||||||
import BitcoinWalletSettings from './bitcoin_wallet_settings';
|
import BitcoinWalletSettings from './bitcoin_wallet_settings';
|
||||||
import APISettings from './api_settings';
|
import APISettings from './api_settings';
|
||||||
|
import WebhookSettings from './webhook_settings';
|
||||||
|
|
||||||
import AclProxy from '../acl_proxy';
|
import AclProxy from '../acl_proxy';
|
||||||
|
|
||||||
@ -70,6 +71,7 @@ let SettingsContainer = React.createClass({
|
|||||||
aclName="acl_view_settings_api">
|
aclName="acl_view_settings_api">
|
||||||
<APISettings />
|
<APISettings />
|
||||||
</AclProxy>
|
</AclProxy>
|
||||||
|
<WebhookSettings />
|
||||||
<AclProxy
|
<AclProxy
|
||||||
aclObject={this.state.whitelabel}
|
aclObject={this.state.whitelabel}
|
||||||
aclName="acl_view_settings_bitcoin">
|
aclName="acl_view_settings_bitcoin">
|
||||||
|
165
js/components/ascribe_settings/webhook_settings.js
Normal file
165
js/components/ascribe_settings/webhook_settings.js
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import WebhookStore from '../../stores/webhook_store';
|
||||||
|
import WebhookActions from '../../actions/webhook_actions';
|
||||||
|
|
||||||
|
import GlobalNotificationModel from '../../models/global_notification_model';
|
||||||
|
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
||||||
|
|
||||||
|
import Form from '../ascribe_forms/form';
|
||||||
|
import Property from '../ascribe_forms/property';
|
||||||
|
|
||||||
|
import AclProxy from '../acl_proxy';
|
||||||
|
|
||||||
|
import ActionPanel from '../ascribe_panel/action_panel';
|
||||||
|
import CollapsibleParagraph from '../ascribe_collapsible/collapsible_paragraph';
|
||||||
|
|
||||||
|
import ApiUrls from '../../constants/api_urls';
|
||||||
|
import AscribeSpinner from '../ascribe_spinner';
|
||||||
|
|
||||||
|
import { getLangText } from '../../utils/lang_utils';
|
||||||
|
|
||||||
|
|
||||||
|
let WebhookSettings = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
defaultExpanded: React.PropTypes.bool
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState() {
|
||||||
|
return WebhookStore.getState();
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
WebhookStore.listen(this.onChange);
|
||||||
|
WebhookActions.fetchWebhooks();
|
||||||
|
WebhookActions.fetchWebhookEvents();
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
WebhookStore.unlisten(this.onChange);
|
||||||
|
},
|
||||||
|
|
||||||
|
onChange(state) {
|
||||||
|
this.setState(state);
|
||||||
|
},
|
||||||
|
|
||||||
|
onRemoveWebhook(webhookId) {
|
||||||
|
return (event) => {
|
||||||
|
WebhookActions.removeWebhook(webhookId);
|
||||||
|
|
||||||
|
let notification = new GlobalNotificationModel(getLangText('Webhook deleted'), 'success', 2000);
|
||||||
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCreateSuccess() {
|
||||||
|
this.refs.webhookCreateForm.reset();
|
||||||
|
WebhookActions.fetchWebhooks(true);
|
||||||
|
let notification = new GlobalNotificationModel(getLangText('Webhook successfully created'), 'success', 5000);
|
||||||
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
|
},
|
||||||
|
|
||||||
|
getWebhooks(){
|
||||||
|
let content = <AscribeSpinner color='dark-blue' size='lg'/>;
|
||||||
|
|
||||||
|
if (this.state.webhooks) {
|
||||||
|
content = this.state.webhooks.map(function(webhook, i) {
|
||||||
|
const event = webhook.event.split('.')[0];
|
||||||
|
return (
|
||||||
|
<ActionPanel
|
||||||
|
name={webhook.event}
|
||||||
|
key={i}
|
||||||
|
content={
|
||||||
|
<div>
|
||||||
|
<div className='ascribe-panel-title'>
|
||||||
|
{event.toUpperCase()}
|
||||||
|
</div>
|
||||||
|
<div className="ascribe-panel-subtitle">
|
||||||
|
{webhook.target}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
buttons={
|
||||||
|
<div className="pull-right">
|
||||||
|
<div className="pull-right">
|
||||||
|
<button
|
||||||
|
className="pull-right btn btn-tertiary btn-sm"
|
||||||
|
onClick={this.onRemoveWebhook(webhook.id)}>
|
||||||
|
{getLangText('DELETE')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}/>
|
||||||
|
);
|
||||||
|
}, this);
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
},
|
||||||
|
|
||||||
|
getEvents() {
|
||||||
|
if (this.state.webhookEvents && this.state.webhookEvents.length) {
|
||||||
|
return (
|
||||||
|
<Property
|
||||||
|
name='event'
|
||||||
|
label={getLangText('Select the event to trigger a webhook', '...')}>
|
||||||
|
<select name="events">
|
||||||
|
{this.state.webhookEvents.map((event, i) => {
|
||||||
|
return (
|
||||||
|
<option
|
||||||
|
name={i}
|
||||||
|
key={i}
|
||||||
|
value={ event + '.webhook' }>
|
||||||
|
{ event.toUpperCase() }
|
||||||
|
</option>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</select>
|
||||||
|
</Property>);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<CollapsibleParagraph
|
||||||
|
title={getLangText('Webhooks')}
|
||||||
|
defaultExpanded={this.props.defaultExpanded}>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Webhooks allow external services to receive notifications from ascribe.
|
||||||
|
Currently we support webhook notifications when someone transfers, consigns, loans or shares
|
||||||
|
(by email) a work to you.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
To get started, simply choose the prefered action that you want to be notified upon and supply
|
||||||
|
a target url.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<AclProxy
|
||||||
|
show={this.state.webhookEvents && this.state.webhookEvents.length}>
|
||||||
|
<Form
|
||||||
|
ref="webhookCreateForm"
|
||||||
|
url={ApiUrls.webhooks}
|
||||||
|
handleSuccess={this.handleCreateSuccess}>
|
||||||
|
{ this.getEvents() }
|
||||||
|
<Property
|
||||||
|
name='target'
|
||||||
|
label={getLangText('Redirect Url')}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder={getLangText('Enter the url to be triggered')}
|
||||||
|
required/>
|
||||||
|
</Property>
|
||||||
|
<hr />
|
||||||
|
</Form>
|
||||||
|
</AclProxy>
|
||||||
|
{this.getWebhooks()}
|
||||||
|
</CollapsibleParagraph>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default WebhookSettings;
|
@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react/addons';
|
import React from 'react/addons';
|
||||||
import { History } from 'react-router';
|
import { History, Lifecycle } from 'react-router';
|
||||||
|
|
||||||
import SlidesContainerBreadcrumbs from './slides_container_breadcrumbs';
|
import SlidesContainerBreadcrumbs from './slides_container_breadcrumbs';
|
||||||
|
|
||||||
@ -17,14 +17,16 @@ const SlidesContainer = React.createClass({
|
|||||||
pending: string,
|
pending: string,
|
||||||
complete: string
|
complete: string
|
||||||
}),
|
}),
|
||||||
location: object
|
location: object,
|
||||||
|
pageExitWarning: string
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [History],
|
mixins: [History, Lifecycle],
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return {
|
return {
|
||||||
containerWidth: 0
|
containerWidth: 0,
|
||||||
|
pageExitWarning: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -41,6 +43,10 @@ const SlidesContainer = React.createClass({
|
|||||||
window.removeEventListener('resize', this.handleContainerResize);
|
window.removeEventListener('resize', this.handleContainerResize);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
routerWillLeave() {
|
||||||
|
return this.props.pageExitWarning;
|
||||||
|
},
|
||||||
|
|
||||||
handleContainerResize() {
|
handleContainerResize() {
|
||||||
this.setState({
|
this.setState({
|
||||||
// +30 to get rid of the padding of the container which is 15px + 15px left and right
|
// +30 to get rid of the padding of the container which is 15px + 15px left and right
|
||||||
|
@ -8,7 +8,6 @@ import { InjectInHeadUtils } from '../../utils/inject_utils';
|
|||||||
|
|
||||||
let FacebookShareButton = React.createClass({
|
let FacebookShareButton = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
url: React.PropTypes.string,
|
|
||||||
type: React.PropTypes.string
|
type: React.PropTypes.string
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -28,12 +27,14 @@ let FacebookShareButton = React.createClass({
|
|||||||
* To circumvent this, we always have the sdk parse the entire DOM on the initial load
|
* To circumvent this, we always have the sdk parse the entire DOM on the initial load
|
||||||
* (see FacebookHandler) and then use FB.XFBML.parse() on the mounting component later.
|
* (see FacebookHandler) and then use FB.XFBML.parse() on the mounting component later.
|
||||||
*/
|
*/
|
||||||
if (!InjectInHeadUtils.isPresent('script', AppConstants.facebook.sdkUrl)) {
|
|
||||||
InjectInHeadUtils.inject(AppConstants.facebook.sdkUrl);
|
InjectInHeadUtils
|
||||||
} else {
|
.inject(AppConstants.facebook.sdkUrl)
|
||||||
// Parse() searches the children of the element we give it, not the element itself.
|
.then(() => { FB.XFBML.parse(this.refs.fbShareButton.getDOMNode().parentElement) });
|
||||||
FB.XFBML.parse(this.refs.fbShareButton.getDOMNode().parentElement);
|
},
|
||||||
}
|
|
||||||
|
shouldComponentUpdate(nextProps) {
|
||||||
|
return this.props.type !== nextProps.type;
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -41,7 +42,6 @@ let FacebookShareButton = React.createClass({
|
|||||||
<span
|
<span
|
||||||
ref="fbShareButton"
|
ref="fbShareButton"
|
||||||
className="fb-share-button btn btn-ascribe-social"
|
className="fb-share-button btn btn-ascribe-social"
|
||||||
data-href={this.props.url}
|
|
||||||
data-layout={this.props.type}>
|
data-layout={this.props.type}>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import NotificationStore from '../stores/notification_store';
|
|
||||||
|
|
||||||
import { mergeOptions } from '../utils/general_utils';
|
|
||||||
|
|
||||||
let ContractNotification = React.createClass({
|
|
||||||
getInitialState() {
|
|
||||||
return mergeOptions(
|
|
||||||
NotificationStore.getState()
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
NotificationStore.listen(this.onChange);
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
NotificationStore.unlisten(this.onChange);
|
|
||||||
},
|
|
||||||
|
|
||||||
onChange(state) {
|
|
||||||
this.setState(state);
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
|
|
||||||
return (
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default ContractNotification;
|
|
@ -1,9 +1,10 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
|
|
||||||
|
import history from '../history';
|
||||||
|
|
||||||
import Nav from 'react-bootstrap/lib/Nav';
|
import Nav from 'react-bootstrap/lib/Nav';
|
||||||
import Navbar from 'react-bootstrap/lib/Navbar';
|
import Navbar from 'react-bootstrap/lib/Navbar';
|
||||||
import CollapsibleNav from 'react-bootstrap/lib/CollapsibleNav';
|
import CollapsibleNav from 'react-bootstrap/lib/CollapsibleNav';
|
||||||
@ -58,11 +59,17 @@ let Header = React.createClass({
|
|||||||
UserStore.listen(this.onChange);
|
UserStore.listen(this.onChange);
|
||||||
WhitelabelActions.fetchWhitelabel();
|
WhitelabelActions.fetchWhitelabel();
|
||||||
WhitelabelStore.listen(this.onChange);
|
WhitelabelStore.listen(this.onChange);
|
||||||
|
|
||||||
|
// react-bootstrap 0.25.1 has a bug in which it doesn't
|
||||||
|
// close the mobile expanded navigation after a click by itself.
|
||||||
|
// To get rid of this, we set the state of the component ourselves.
|
||||||
|
history.listen(this.onRouteChange);
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
UserStore.unlisten(this.onChange);
|
UserStore.unlisten(this.onChange);
|
||||||
WhitelabelStore.unlisten(this.onChange);
|
WhitelabelStore.unlisten(this.onChange);
|
||||||
|
history.unlisten(this.onRouteChange);
|
||||||
},
|
},
|
||||||
|
|
||||||
getLogo() {
|
getLogo() {
|
||||||
@ -135,6 +142,13 @@ let Header = React.createClass({
|
|||||||
this.refs.dropdownbutton.setDropdownState(false);
|
this.refs.dropdownbutton.setDropdownState(false);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// On route change, close expanded navbar again since react-bootstrap doesn't close
|
||||||
|
// the collapsibleNav by itself on click. setState() isn't available on a ref so
|
||||||
|
// doing this explicitly is the only way for now.
|
||||||
|
onRouteChange() {
|
||||||
|
this.refs.navbar.state.navExpanded = false;
|
||||||
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let account;
|
let account;
|
||||||
let signup;
|
let signup;
|
||||||
@ -201,8 +215,10 @@ let Header = React.createClass({
|
|||||||
<Navbar
|
<Navbar
|
||||||
brand={this.getLogo()}
|
brand={this.getLogo()}
|
||||||
toggleNavKey={0}
|
toggleNavKey={0}
|
||||||
fixedTop={true}>
|
fixedTop={true}
|
||||||
<CollapsibleNav eventKey={0}>
|
ref="navbar">
|
||||||
|
<CollapsibleNav
|
||||||
|
eventKey={0}>
|
||||||
<Nav navbar left>
|
<Nav navbar left>
|
||||||
{this.getPoweredBy()}
|
{this.getPoweredBy()}
|
||||||
</Nav>
|
</Nav>
|
||||||
|
@ -32,6 +32,7 @@ import ApiUrls from '../../../../../constants/api_urls';
|
|||||||
import { mergeOptions } from '../../../../../utils/general_utils';
|
import { mergeOptions } from '../../../../../utils/general_utils';
|
||||||
import { getLangText } from '../../../../../utils/lang_utils';
|
import { getLangText } from '../../../../../utils/lang_utils';
|
||||||
|
|
||||||
|
|
||||||
let IkonotvRegisterPiece = React.createClass({
|
let IkonotvRegisterPiece = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
handleSuccess: React.PropTypes.func,
|
handleSuccess: React.PropTypes.func,
|
||||||
@ -47,7 +48,8 @@ let IkonotvRegisterPiece = React.createClass({
|
|||||||
PieceListStore.getState(),
|
PieceListStore.getState(),
|
||||||
PieceStore.getState(),
|
PieceStore.getState(),
|
||||||
{
|
{
|
||||||
step: 0
|
step: 0,
|
||||||
|
pageExitWarning: getLangText("If you leave this form now, your work will not be loaned to Ikono TV.")
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -94,7 +96,6 @@ let IkonotvRegisterPiece = React.createClass({
|
|||||||
|
|
||||||
|
|
||||||
handleRegisterSuccess(response){
|
handleRegisterSuccess(response){
|
||||||
|
|
||||||
this.refreshPieceList();
|
this.refreshPieceList();
|
||||||
|
|
||||||
// also start loading the piece for the next step
|
// also start loading the piece for the next step
|
||||||
@ -108,7 +109,6 @@ let IkonotvRegisterPiece = React.createClass({
|
|||||||
this.incrementStep();
|
this.incrementStep();
|
||||||
this.refs.slidesContainer.nextSlide();
|
this.refs.slidesContainer.nextSlide();
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
handleAdditionalDataSuccess() {
|
handleAdditionalDataSuccess() {
|
||||||
@ -126,6 +126,8 @@ let IkonotvRegisterPiece = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleLoanSuccess(response) {
|
handleLoanSuccess(response) {
|
||||||
|
this.setState({ pageExitWarning: null });
|
||||||
|
|
||||||
let notification = new GlobalNotificationModel(response.notification, 'success', 10000);
|
let notification = new GlobalNotificationModel(response.notification, 'success', 10000);
|
||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
|
|
||||||
@ -238,6 +240,8 @@ let IkonotvRegisterPiece = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { pageExitWarning } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SlidesContainer
|
<SlidesContainer
|
||||||
ref="slidesContainer"
|
ref="slidesContainer"
|
||||||
@ -246,7 +250,8 @@ let IkonotvRegisterPiece = React.createClass({
|
|||||||
pending: 'glyphicon glyphicon-chevron-right',
|
pending: 'glyphicon glyphicon-chevron-right',
|
||||||
completed: 'glyphicon glyphicon-lock'
|
completed: 'glyphicon glyphicon-lock'
|
||||||
}}
|
}}
|
||||||
location={this.props.location}>
|
location={this.props.location}
|
||||||
|
pageExitWarning={pageExitWarning}>
|
||||||
<div data-slide-title={getLangText('Register work')}>
|
<div data-slide-title={getLangText('Register work')}>
|
||||||
<Row className="no-margin">
|
<Row className="no-margin">
|
||||||
<Col xs={12} sm={10} md={8} smOffset={1} mdOffset={2}>
|
<Col xs={12} sm={10} md={8} smOffset={1} mdOffset={2}>
|
||||||
|
@ -72,6 +72,9 @@ let ApiUrls = {
|
|||||||
'users_username': AppConstants.apiEndpoint + 'users/username/',
|
'users_username': AppConstants.apiEndpoint + 'users/username/',
|
||||||
'users_profile': AppConstants.apiEndpoint + 'users/profile/',
|
'users_profile': AppConstants.apiEndpoint + 'users/profile/',
|
||||||
'wallet_settings': AppConstants.apiEndpoint + 'users/wallet_settings/',
|
'wallet_settings': AppConstants.apiEndpoint + 'users/wallet_settings/',
|
||||||
|
'webhook': AppConstants.apiEndpoint + 'webhooks/${webhook_id}/',
|
||||||
|
'webhooks': AppConstants.apiEndpoint + 'webhooks/',
|
||||||
|
'webhooks_events': AppConstants.apiEndpoint + 'webhooks/events/',
|
||||||
'whitelabel_settings': AppConstants.apiEndpoint + 'whitelabel/settings/${subdomain}/',
|
'whitelabel_settings': AppConstants.apiEndpoint + 'whitelabel/settings/${subdomain}/',
|
||||||
'delete_s3_file': AppConstants.serverUrl + 's3/delete/',
|
'delete_s3_file': AppConstants.serverUrl + 's3/delete/',
|
||||||
'prize_list': AppConstants.apiEndpoint + 'prize/'
|
'prize_list': AppConstants.apiEndpoint + 'prize/'
|
||||||
|
@ -114,9 +114,6 @@ const constants = {
|
|||||||
},
|
},
|
||||||
'twitter': {
|
'twitter': {
|
||||||
'sdkUrl': 'https://platform.twitter.com/widgets.js'
|
'sdkUrl': 'https://platform.twitter.com/widgets.js'
|
||||||
},
|
|
||||||
'fartscroll': {
|
|
||||||
'sdkUrl': 'http://code.onion.com/fartscroll.js'
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
46
js/sources/webhook_source.js
Normal file
46
js/sources/webhook_source.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import requests from '../utils/requests';
|
||||||
|
|
||||||
|
import WebhookActions from '../actions/webhook_actions';
|
||||||
|
|
||||||
|
|
||||||
|
const WebhookSource = {
|
||||||
|
lookupWebhooks: {
|
||||||
|
remote() {
|
||||||
|
return requests.get('webhooks');
|
||||||
|
},
|
||||||
|
local(state) {
|
||||||
|
return state.webhooks && !Object.keys(state.webhooks).length ? state : {};
|
||||||
|
},
|
||||||
|
success: WebhookActions.successFetchWebhooks,
|
||||||
|
error: WebhookActions.errorWebhooks,
|
||||||
|
shouldFetch(state) {
|
||||||
|
return state.webhookMeta.invalidateCache || state.webhooks && !Object.keys(state.webhooks).length;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
lookupWebhookEvents: {
|
||||||
|
remote() {
|
||||||
|
return requests.get('webhooks_events');
|
||||||
|
},
|
||||||
|
local(state) {
|
||||||
|
return state.webhookEvents && !Object.keys(state.webhookEvents).length ? state : {};
|
||||||
|
},
|
||||||
|
success: WebhookActions.successFetchWebhookEvents,
|
||||||
|
error: WebhookActions.errorWebhookEvents,
|
||||||
|
shouldFetch(state) {
|
||||||
|
return state.webhookEventsMeta.invalidateCache || state.webhookEvents && !Object.keys(state.webhookEvents).length;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
performRemoveWebhook: {
|
||||||
|
remote(state) {
|
||||||
|
return requests.delete('webhook', {'webhook_id': state.webhookMeta.idToDelete });
|
||||||
|
},
|
||||||
|
success: WebhookActions.successRemoveWebhook,
|
||||||
|
error: WebhookActions.errorWebhooks
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WebhookSource;
|
88
js/stores/webhook_store.js
Normal file
88
js/stores/webhook_store.js
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import { alt } from '../alt';
|
||||||
|
import WebhookActions from '../actions/webhook_actions';
|
||||||
|
|
||||||
|
import WebhookSource from '../sources/webhook_source';
|
||||||
|
|
||||||
|
class WebhookStore {
|
||||||
|
constructor() {
|
||||||
|
this.webhooks = [];
|
||||||
|
this.webhookEvents = [];
|
||||||
|
this.webhookMeta = {
|
||||||
|
invalidateCache: false,
|
||||||
|
err: null,
|
||||||
|
idToDelete: null
|
||||||
|
};
|
||||||
|
this.webhookEventsMeta = {
|
||||||
|
invalidateCache: false,
|
||||||
|
err: null
|
||||||
|
};
|
||||||
|
|
||||||
|
this.bindActions(WebhookActions);
|
||||||
|
this.registerAsync(WebhookSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
onFetchWebhooks(invalidateCache) {
|
||||||
|
this.webhookMeta.invalidateCache = invalidateCache;
|
||||||
|
this.getInstance().lookupWebhooks();
|
||||||
|
}
|
||||||
|
|
||||||
|
onSuccessFetchWebhooks({ webhooks }) {
|
||||||
|
this.webhookMeta.invalidateCache = false;
|
||||||
|
this.webhookMeta.err = null;
|
||||||
|
this.webhooks = webhooks;
|
||||||
|
|
||||||
|
this.webhookEventsMeta.invalidateCache = true;
|
||||||
|
this.getInstance().lookupWebhookEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
onFetchWebhookEvents(invalidateCache) {
|
||||||
|
this.webhookEventsMeta.invalidateCache = invalidateCache;
|
||||||
|
this.getInstance().lookupWebhookEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
onSuccessFetchWebhookEvents({ events }) {
|
||||||
|
this.webhookEventsMeta.invalidateCache = false;
|
||||||
|
this.webhookEventsMeta.err = null;
|
||||||
|
|
||||||
|
// remove all events that have already been used.
|
||||||
|
const usedEvents = this.webhooks
|
||||||
|
.reduce((tempUsedEvents, webhook) => {
|
||||||
|
tempUsedEvents.push(webhook.event.split('.')[0]);
|
||||||
|
return tempUsedEvents;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
this.webhookEvents = events.filter((event) => {
|
||||||
|
return usedEvents.indexOf(event) === -1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onRemoveWebhook(id) {
|
||||||
|
this.webhookMeta.invalidateCache = true;
|
||||||
|
this.webhookMeta.idToDelete = id;
|
||||||
|
|
||||||
|
if(!this.getInstance().isLoading()) {
|
||||||
|
this.getInstance().performRemoveWebhook();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onSuccessRemoveWebhook() {
|
||||||
|
this.webhookMeta.idToDelete = null;
|
||||||
|
if(!this.getInstance().isLoading()) {
|
||||||
|
this.getInstance().lookupWebhooks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onErrorWebhooks(err) {
|
||||||
|
console.logGlobal(err);
|
||||||
|
this.webhookMeta.err = err;
|
||||||
|
}
|
||||||
|
|
||||||
|
onErrorWebhookEvents(err) {
|
||||||
|
console.logGlobal(err);
|
||||||
|
this.webhookEventsMeta.err = err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default alt.createStore(WebhookStore, 'WebhookStore');
|
@ -12,16 +12,16 @@ let mapTag = {
|
|||||||
css: 'link'
|
css: 'link'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let tags = {};
|
||||||
|
|
||||||
function injectTag(tag, src) {
|
function injectTag(tag, src) {
|
||||||
return Q.Promise((resolve, reject) => {
|
if(!tags[src]) {
|
||||||
if (isPresent(tag, src)) {
|
tags[src] = Q.Promise((resolve, reject) => {
|
||||||
resolve();
|
|
||||||
} else {
|
|
||||||
let attr = mapAttr[tag];
|
let attr = mapAttr[tag];
|
||||||
let element = document.createElement(tag);
|
let element = document.createElement(tag);
|
||||||
if (tag === 'script') {
|
if (tag === 'script') {
|
||||||
element.onload = () => resolve();
|
element.onload = resolve;
|
||||||
element.onerror = () => reject();
|
element.onerror = reject;
|
||||||
} else {
|
} else {
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
@ -30,14 +30,10 @@ function injectTag(tag, src) {
|
|||||||
if (tag === 'link') {
|
if (tag === 'link') {
|
||||||
element.rel = 'stylesheet';
|
element.rel = 'stylesheet';
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function isPresent(tag, src) {
|
return tags[src];
|
||||||
let attr = mapAttr[tag];
|
|
||||||
let query = `head > ${tag}[${attr}="${src}"]`;
|
|
||||||
return document.querySelector(query);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function injectStylesheet(src) {
|
function injectStylesheet(src) {
|
||||||
@ -65,7 +61,6 @@ export const InjectInHeadUtils = {
|
|||||||
* you don't want to embed everything inside the build file.
|
* you don't want to embed everything inside the build file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
isPresent,
|
|
||||||
injectStylesheet,
|
injectStylesheet,
|
||||||
injectScript,
|
injectScript,
|
||||||
inject
|
inject
|
||||||
|
@ -66,7 +66,7 @@
|
|||||||
"gulp-uglify": "^1.2.0",
|
"gulp-uglify": "^1.2.0",
|
||||||
"gulp-util": "^3.0.4",
|
"gulp-util": "^3.0.4",
|
||||||
"harmonize": "^1.4.2",
|
"harmonize": "^1.4.2",
|
||||||
"history": "^1.11.1",
|
"history": "^1.13.1",
|
||||||
"invariant": "^2.1.1",
|
"invariant": "^2.1.1",
|
||||||
"isomorphic-fetch": "^2.0.2",
|
"isomorphic-fetch": "^2.0.2",
|
||||||
"jest-cli": "^0.4.0",
|
"jest-cli": "^0.4.0",
|
||||||
@ -80,7 +80,7 @@
|
|||||||
"react": "0.13.2",
|
"react": "0.13.2",
|
||||||
"react-bootstrap": "0.25.1",
|
"react-bootstrap": "0.25.1",
|
||||||
"react-datepicker": "^0.12.0",
|
"react-datepicker": "^0.12.0",
|
||||||
"react-router": "^1.0.0-rc3",
|
"react-router": "1.0.0",
|
||||||
"react-router-bootstrap": "^0.19.0",
|
"react-router-bootstrap": "^0.19.0",
|
||||||
"react-star-rating": "~1.3.2",
|
"react-star-rating": "~1.3.2",
|
||||||
"react-textarea-autosize": "^2.5.2",
|
"react-textarea-autosize": "^2.5.2",
|
||||||
|
@ -2,8 +2,9 @@ $break-small: 764px;
|
|||||||
$break-medium: 991px;
|
$break-medium: 991px;
|
||||||
$break-medium: 1200px;
|
$break-medium: 1200px;
|
||||||
|
|
||||||
.notification-header,.notification-wrapper {
|
.notification-header, .notification-wrapper {
|
||||||
width: 350px;
|
min-width: 350px;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-header {
|
.notification-header {
|
||||||
|
Loading…
Reference in New Issue
Block a user