mirror of
https://github.com/ascribe/onion.git
synced 2024-12-22 17:33:14 +01:00
Merge branch 'master' into AD-1149-implement-lumenus-the-lumen-mark
This commit is contained in:
commit
efdc5d32e7
@ -10,6 +10,7 @@ queryParams of the piece_list_store should all be reflected in the url and not a
|
|||||||
- Use classNames plugin instead of if-conditional-classes
|
- Use classNames plugin instead of if-conditional-classes
|
||||||
- Instead of using `currentUser && currentUser.email` in an validation that checks whether we user is logged in or now, in the `UserStore` on login we set a boolean property called `isLoggedIn` that can then be used instead of `email`
|
- Instead of using `currentUser && currentUser.email` in an validation that checks whether we user is logged in or now, in the `UserStore` on login we set a boolean property called `isLoggedIn` that can then be used instead of `email`
|
||||||
- Refactor AclProxy to be a generic hide/show element component. Have it take data input and a validation function to assess whether it should show or hide child elements. Move current Acl checks to another place, eg. acl_utils.js.
|
- Refactor AclProxy to be a generic hide/show element component. Have it take data input and a validation function to assess whether it should show or hide child elements. Move current Acl checks to another place, eg. acl_utils.js.
|
||||||
|
- Convert all fetchers to [alt.js's sources](http://alt.js.org/docs/async/)
|
||||||
|
|
||||||
# Refactor DONE
|
# Refactor DONE
|
||||||
- Refactor forms to generic-declarative form component ✓
|
- Refactor forms to generic-declarative form component ✓
|
||||||
|
@ -1,37 +1,18 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { altUser } from '../alt';
|
import { altUser } from '../alt';
|
||||||
import UserFetcher from '../fetchers/user_fetcher';
|
|
||||||
|
|
||||||
|
|
||||||
class UserActions {
|
class UserActions {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.generateActions(
|
this.generateActions(
|
||||||
'updateCurrentUser',
|
'fetchCurrentUser',
|
||||||
'deleteCurrentUser'
|
'successFetchCurrentUser',
|
||||||
|
'logoutCurrentUser',
|
||||||
|
'successLogoutCurrentUser',
|
||||||
|
'errorCurrentUser'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchCurrentUser() {
|
|
||||||
UserFetcher.fetchOne()
|
|
||||||
.then((res) => {
|
|
||||||
this.actions.updateCurrentUser(res.users[0]);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.logGlobal(err);
|
|
||||||
this.actions.updateCurrentUser({});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
logoutCurrentUser() {
|
|
||||||
UserFetcher.logout()
|
|
||||||
.then(() => {
|
|
||||||
this.actions.deleteCurrentUser();
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.logGlobal(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default altUser.createActions(UserActions);
|
export default altUser.createActions(UserActions);
|
||||||
|
@ -1,29 +1,16 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { altWhitelabel } from '../alt';
|
import { altWhitelabel } from '../alt';
|
||||||
import WhitelabelFetcher from '../fetchers/whitelabel_fetcher';
|
|
||||||
|
|
||||||
|
|
||||||
class WhitelabelActions {
|
class WhitelabelActions {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.generateActions(
|
this.generateActions(
|
||||||
'updateWhitelabel'
|
'fetchWhitelabel',
|
||||||
|
'successFetchWhitelabel',
|
||||||
|
'errorWhitelabel'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchWhitelabel() {
|
|
||||||
WhitelabelFetcher.fetch()
|
|
||||||
.then((res) => {
|
|
||||||
if(res && res.whitelabel) {
|
|
||||||
this.actions.updateWhitelabel(res.whitelabel);
|
|
||||||
} else {
|
|
||||||
this.actions.updateWhitelabel({});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.logGlobal(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default altWhitelabel.createActions(WhitelabelActions);
|
export default altWhitelabel.createActions(WhitelabelActions);
|
||||||
|
@ -30,6 +30,7 @@ import GoogleAnalyticsHandler from './third_party/ga';
|
|||||||
import RavenHandler from './third_party/raven';
|
import RavenHandler from './third_party/raven';
|
||||||
import IntercomHandler from './third_party/intercom';
|
import IntercomHandler from './third_party/intercom';
|
||||||
import NotificationsHandler from './third_party/notifications';
|
import NotificationsHandler from './third_party/notifications';
|
||||||
|
import FacebookHandler from './third_party/facebook';
|
||||||
/* eslint-enable */
|
/* eslint-enable */
|
||||||
|
|
||||||
initLogging();
|
initLogging();
|
||||||
|
@ -6,9 +6,9 @@ import UserActions from '../../actions/user_actions';
|
|||||||
import UserStore from '../../stores/user_store';
|
import UserStore from '../../stores/user_store';
|
||||||
|
|
||||||
import ConsignButton from './acls/consign_button';
|
import ConsignButton from './acls/consign_button';
|
||||||
|
import EmailButton from './acls/email_button';
|
||||||
import LoanButton from './acls/loan_button';
|
import LoanButton from './acls/loan_button';
|
||||||
import LoanRequestButton from './acls/loan_request_button';
|
import LoanRequestButton from './acls/loan_request_button';
|
||||||
import ShareButton from './acls/share_button';
|
|
||||||
import TransferButton from './acls/transfer_button';
|
import TransferButton from './acls/transfer_button';
|
||||||
import UnconsignButton from './acls/unconsign_button';
|
import UnconsignButton from './acls/unconsign_button';
|
||||||
|
|
||||||
@ -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'));
|
||||||
@ -90,7 +90,7 @@ let AclButtonList = React.createClass({
|
|||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<span ref="buttonList" style={buttonsStyle}>
|
<span ref="buttonList" style={buttonsStyle}>
|
||||||
<ShareButton
|
<EmailButton
|
||||||
availableAcls={availableAcls}
|
availableAcls={availableAcls}
|
||||||
pieceOrEditions={pieceOrEditions}
|
pieceOrEditions={pieceOrEditions}
|
||||||
currentUser={currentUser}
|
currentUser={currentUser}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
|
||||||
import { InformationTexts } from '../../constants/information_text';
|
import { AclInformationText } from '../../constants/acl_information_text';
|
||||||
import { replaceSubstringAtIndex, sanitize, intersectLists } from '../../utils/general_utils';
|
import { replaceSubstringAtIndex, sanitize, intersectLists } from '../../utils/general_utils';
|
||||||
import { getLangText } from '../../utils/lang_utils';
|
import { getLangText } from '../../utils/lang_utils';
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ let AclInformation = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
getInfoText(title, info, example){
|
getInfoText(title, info, example){
|
||||||
let aim = this.props.aim;
|
const aim = this.props.aim;
|
||||||
|
|
||||||
if(aim) {
|
if(aim) {
|
||||||
if(aim === 'form') {
|
if(aim === 'form') {
|
||||||
@ -75,7 +75,7 @@ let AclInformation = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
produceInformationBlock() {
|
produceInformationBlock() {
|
||||||
const { titles, informationSentences, exampleSentences } = InformationTexts;
|
const { titles, informationSentences, exampleSentences } = AclInformationText;
|
||||||
const { verbs, aim } = this.props;
|
const { verbs, aim } = this.props;
|
||||||
|
|
||||||
const availableInformations = intersectLists(verbs, Object.keys(titles));
|
const availableInformations = intersectLists(verbs, Object.keys(titles));
|
||||||
@ -95,7 +95,13 @@ let AclInformation = React.createClass({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return verbsToDisplay.map((verb) => {
|
return verbsToDisplay.map((verb) => {
|
||||||
return this.getInfoText(getLangText(titles[verb]), getLangText(informationSentences[verb]), getLangText(exampleSentences[verb]));
|
const title = titles[verb];
|
||||||
|
const informationSentence = informationSentences[verb];
|
||||||
|
const exampleSentence = exampleSentences[verb];
|
||||||
|
|
||||||
|
if (title && informationSentence && exampleSentence) {
|
||||||
|
return this.getInfoText(getLangText(title), getLangText(informationSentence), getLangText(exampleSentence));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -11,6 +11,8 @@ import ModalWrapper from '../../ascribe_modal/modal_wrapper';
|
|||||||
|
|
||||||
import AppConstants from '../../../constants/application_constants';
|
import AppConstants from '../../../constants/application_constants';
|
||||||
|
|
||||||
|
import { AclInformationText } from '../../../constants/acl_information_text';
|
||||||
|
|
||||||
|
|
||||||
export default function ({ action, displayName, title, tooltip }) {
|
export default function ({ action, displayName, title, tooltip }) {
|
||||||
if (AppConstants.aclList.indexOf(action) < 0) {
|
if (AppConstants.aclList.indexOf(action) < 0) {
|
||||||
@ -34,12 +36,11 @@ export default function ({ action, displayName, title, tooltip }) {
|
|||||||
className: React.PropTypes.string
|
className: React.PropTypes.string
|
||||||
},
|
},
|
||||||
|
|
||||||
// Removes the acl_ prefix and converts to upper case
|
|
||||||
sanitizeAction() {
|
sanitizeAction() {
|
||||||
if (this.props.buttonAcceptName) {
|
if (this.props.buttonAcceptName) {
|
||||||
return this.props.buttonAcceptName;
|
return this.props.buttonAcceptName;
|
||||||
}
|
}
|
||||||
return action.split('acl_')[1].toUpperCase();
|
return AclInformationText.titles[action];
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -8,7 +8,7 @@ import { getLangText } from '../../../utils/lang_utils';
|
|||||||
|
|
||||||
export default AclButton({
|
export default AclButton({
|
||||||
action: 'acl_share',
|
action: 'acl_share',
|
||||||
displayName: 'ShareButton',
|
displayName: 'EmailButton',
|
||||||
title: getLangText('Share artwork'),
|
title: getLangText('Share artwork via email'),
|
||||||
tooltip: getLangText('Share the artwork')
|
tooltip: getLangText("Share the artwork to another user's collection through email")
|
||||||
});
|
});
|
@ -21,13 +21,13 @@ let CollapsibleButton = React.createClass({
|
|||||||
this.setState({expanded: !this.state.expanded});
|
this.setState({expanded: !this.state.expanded});
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
let isVisible = (this.state.expanded) ? '' : 'invisible';
|
let isHidden = (this.state.expanded) ? '' : 'hidden';
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
<span onClick={this.handleToggle}>
|
<span onClick={this.handleToggle}>
|
||||||
{this.props.button}
|
{this.props.button}
|
||||||
</span>
|
</span>
|
||||||
<div ref='panel' className={isVisible}>
|
<div ref='panel' className={isHidden}>
|
||||||
{this.props.panel}
|
{this.props.panel}
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
@ -172,7 +172,7 @@ let EditionActionPanel = React.createClass({
|
|||||||
editions={[edition]}/>
|
editions={[edition]}/>
|
||||||
<AclInformation
|
<AclInformation
|
||||||
aim="button"
|
aim="button"
|
||||||
verbs={['acl_share', 'acl_consign', 'acl_loan', 'acl_delete']}
|
verbs={['acl_share', 'acl_transfer', 'acl_consign', 'acl_loan', 'acl_delete']}
|
||||||
aclObject={edition.acl}/>
|
aclObject={edition.acl}/>
|
||||||
</ActionPanelButtonListType>
|
</ActionPanelButtonListType>
|
||||||
</Col>
|
</Col>
|
||||||
|
@ -5,11 +5,33 @@ import React from 'react';
|
|||||||
import Form from '../ascribe_forms/form';
|
import Form from '../ascribe_forms/form';
|
||||||
import Property from '../ascribe_forms/property';
|
import Property from '../ascribe_forms/property';
|
||||||
|
|
||||||
|
import { replaceSubstringAtIndex } from '../../utils/general_utils';
|
||||||
|
|
||||||
|
|
||||||
let HistoryIterator = React.createClass({
|
let HistoryIterator = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
history: React.PropTypes.array
|
history: React.PropTypes.array
|
||||||
},
|
},
|
||||||
|
|
||||||
|
composeHistoryDescription(historicalEvent) {
|
||||||
|
if(historicalEvent.length === 3) {
|
||||||
|
// We want to get the capturing group without the quotes,
|
||||||
|
// which is why we access the match list at index 1 and not 0
|
||||||
|
const contractName = historicalEvent[1].match(/\"(.*)\"/)[1];
|
||||||
|
const historicalEventDescription = replaceSubstringAtIndex(historicalEvent[1], `"${contractName}"`, '');
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
{historicalEventDescription}
|
||||||
|
<a href={historicalEvent[2]} target="_blank">{contractName}</a>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
} else if(historicalEvent.length === 2) {
|
||||||
|
return historicalEvent[1];
|
||||||
|
} else {
|
||||||
|
throw new Error('Expected an historical event list with either 3 or 2 items. Got less or more.');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Form>
|
<Form>
|
||||||
@ -20,7 +42,7 @@ let HistoryIterator = React.createClass({
|
|||||||
key={i}
|
key={i}
|
||||||
label={ historicalEvent[0] }
|
label={ historicalEvent[0] }
|
||||||
editable={false}>
|
editable={false}>
|
||||||
<pre className="ascribe-pre">{ historicalEvent[1] }</pre>
|
<pre className="ascribe-pre">{this.composeHistoryDescription(historicalEvent)}</pre>
|
||||||
</Property>
|
</Property>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -7,10 +7,18 @@ import Glyphicon from 'react-bootstrap/lib/Glyphicon';
|
|||||||
|
|
||||||
import MediaPlayer from './../ascribe_media/media_player';
|
import MediaPlayer from './../ascribe_media/media_player';
|
||||||
|
|
||||||
|
import FacebookShareButton from '../ascribe_social_share/facebook_share_button';
|
||||||
|
import TwitterShareButton from '../ascribe_social_share/twitter_share_button';
|
||||||
|
|
||||||
import CollapsibleButton from './../ascribe_collapsible/collapsible_button';
|
import CollapsibleButton from './../ascribe_collapsible/collapsible_button';
|
||||||
|
|
||||||
import AclProxy from '../acl_proxy';
|
import AclProxy from '../acl_proxy';
|
||||||
|
|
||||||
|
import UserActions from '../../actions/user_actions';
|
||||||
|
import UserStore from '../../stores/user_store';
|
||||||
|
|
||||||
|
import { mergeOptions } from '../../utils/general_utils.js';
|
||||||
|
import { getLangText } from '../../utils/lang_utils.js';
|
||||||
|
|
||||||
const EMBED_IFRAME_HEIGHT = {
|
const EMBED_IFRAME_HEIGHT = {
|
||||||
video: 315,
|
video: 315,
|
||||||
@ -24,10 +32,17 @@ let MediaContainer = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return {timerId: null};
|
return mergeOptions(
|
||||||
|
UserStore.getState(),
|
||||||
|
{
|
||||||
|
timerId: null
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
UserStore.listen(this.onChange);
|
||||||
|
UserActions.fetchCurrentUser();
|
||||||
|
|
||||||
if (!this.props.content.digital_work) {
|
if (!this.props.content.digital_work) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -45,19 +60,32 @@ let MediaContainer = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
UserStore.unlisten(this.onChange);
|
||||||
|
|
||||||
window.clearInterval(this.state.timerId);
|
window.clearInterval(this.state.timerId);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onChange(state) {
|
||||||
|
this.setState(state);
|
||||||
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let thumbnail = this.props.content.thumbnail.thumbnail_sizes && this.props.content.thumbnail.thumbnail_sizes['600x600'] ?
|
const { content } = this.props;
|
||||||
this.props.content.thumbnail.thumbnail_sizes['600x600'] : this.props.content.thumbnail.url_safe;
|
// Pieces and editions are joined to the user by a foreign key in the database, so
|
||||||
let mimetype = this.props.content.digital_work.mime;
|
// the information in content will be updated if a user updates their username.
|
||||||
|
// We also force uniqueness of usernames, so this check is safe to dtermine if the
|
||||||
|
// content was registered by the current user.
|
||||||
|
const didUserRegisterContent = this.state.currentUser && (this.state.currentUser.username === content.user_registered);
|
||||||
|
|
||||||
|
let thumbnail = content.thumbnail.thumbnail_sizes && content.thumbnail.thumbnail_sizes['600x600'] ?
|
||||||
|
content.thumbnail.thumbnail_sizes['600x600'] : content.thumbnail.url_safe;
|
||||||
|
let mimetype = content.digital_work.mime;
|
||||||
let embed = null;
|
let embed = null;
|
||||||
let extraData = null;
|
let extraData = null;
|
||||||
let isEmbedDisabled = mimetype === 'video' && this.props.content.digital_work.isEncoding !== undefined && this.props.content.digital_work.isEncoding !== 100;
|
let isEmbedDisabled = mimetype === 'video' && content.digital_work.isEncoding !== undefined && content.digital_work.isEncoding !== 100;
|
||||||
|
|
||||||
if (this.props.content.digital_work.encoding_urls) {
|
if (content.digital_work.encoding_urls) {
|
||||||
extraData = this.props.content.digital_work.encoding_urls.map(e => { return { url: e.url, type: e.label }; });
|
extraData = content.digital_work.encoding_urls.map(e => { return { url: e.url, type: e.label }; });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (['video', 'audio'].indexOf(mimetype) > -1) {
|
if (['video', 'audio'].indexOf(mimetype) > -1) {
|
||||||
@ -73,7 +101,7 @@ let MediaContainer = React.createClass({
|
|||||||
panel={
|
panel={
|
||||||
<pre className="">
|
<pre className="">
|
||||||
{'<iframe width="560" height="' + height + '" src="https://embed.ascribe.io/content/'
|
{'<iframe width="560" height="' + height + '" src="https://embed.ascribe.io/content/'
|
||||||
+ this.props.content.bitcoin_id + '" frameborder="0" allowfullscreen></iframe>'}
|
+ content.bitcoin_id + '" frameborder="0" allowfullscreen></iframe>'}
|
||||||
</pre>
|
</pre>
|
||||||
}/>
|
}/>
|
||||||
);
|
);
|
||||||
@ -83,13 +111,19 @@ let MediaContainer = React.createClass({
|
|||||||
<MediaPlayer
|
<MediaPlayer
|
||||||
mimetype={mimetype}
|
mimetype={mimetype}
|
||||||
preview={thumbnail}
|
preview={thumbnail}
|
||||||
url={this.props.content.digital_work.url}
|
url={content.digital_work.url}
|
||||||
extraData={extraData}
|
extraData={extraData}
|
||||||
encodingStatus={this.props.content.digital_work.isEncoding} />
|
encodingStatus={content.digital_work.isEncoding} />
|
||||||
<p className="text-center">
|
<p className="text-center">
|
||||||
|
<span className="ascribe-social-button-list">
|
||||||
|
<FacebookShareButton />
|
||||||
|
<TwitterShareButton
|
||||||
|
text={getLangText('Check out %s ascribed piece', didUserRegisterContent ? 'my latest' : 'this' )} />
|
||||||
|
</span>
|
||||||
|
|
||||||
<AclProxy
|
<AclProxy
|
||||||
show={['video', 'audio', 'image'].indexOf(mimetype) === -1 || this.props.content.acl.acl_download}
|
show={['video', 'audio', 'image'].indexOf(mimetype) === -1 || content.acl.acl_download}
|
||||||
aclObject={this.props.content.acl}
|
aclObject={content.acl}
|
||||||
aclName="acl_download">
|
aclName="acl_download">
|
||||||
<Button bsSize="xsmall" className="ascribe-margin-1px" href={this.props.content.digital_work.url} target="_blank">
|
<Button bsSize="xsmall" className="ascribe-margin-1px" href={this.props.content.digital_work.url} target="_blank">
|
||||||
Download .{mimetype} <Glyphicon glyph="cloud-download"/>
|
Download .{mimetype} <Glyphicon glyph="cloud-download"/>
|
||||||
|
@ -222,7 +222,8 @@ let PieceContainer = React.createClass({
|
|||||||
piece={piece}/>
|
piece={piece}/>
|
||||||
<AclInformation
|
<AclInformation
|
||||||
aim="button"
|
aim="button"
|
||||||
verbs={['acl_share', 'acl_create_editions', 'acl_loan', 'acl_delete', 'acl_consign']}
|
verbs={['acl_share', 'acl_transfer', 'acl_create_editions', 'acl_loan', 'acl_delete',
|
||||||
|
'acl_consign']}
|
||||||
aclObject={piece.acl}/>
|
aclObject={piece.acl}/>
|
||||||
</AclButtonList>
|
</AclButtonList>
|
||||||
</DetailProperty>
|
</DetailProperty>
|
||||||
|
@ -60,7 +60,7 @@ let LoginForm = React.createClass({
|
|||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
|
|
||||||
if(success) {
|
if(success) {
|
||||||
UserActions.fetchCurrentUser();
|
UserActions.fetchCurrentUser(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ let SignupForm = React.createClass({
|
|||||||
// Refactor this to its own component
|
// Refactor this to its own component
|
||||||
this.props.handleSuccess(getLangText('We sent an email to your address') + ' ' + response.user.email + ', ' + getLangText('please confirm') + '.');
|
this.props.handleSuccess(getLangText('We sent an email to your address') + ' ' + response.user.email + ', ' + getLangText('please confirm') + '.');
|
||||||
} else {
|
} else {
|
||||||
UserActions.fetchCurrentUser();
|
UserActions.fetchCurrentUser(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -3,12 +3,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Q from 'q';
|
import Q from 'q';
|
||||||
|
|
||||||
import { escapeHTML } from '../../utils/general_utils';
|
|
||||||
|
|
||||||
import InjectInHeadMixin from '../../mixins/inject_in_head_mixin';
|
|
||||||
import Panel from 'react-bootstrap/lib/Panel';
|
import Panel from 'react-bootstrap/lib/Panel';
|
||||||
import ProgressBar from 'react-bootstrap/lib/ProgressBar';
|
import ProgressBar from 'react-bootstrap/lib/ProgressBar';
|
||||||
import AppConstants from '../../constants/application_constants.js';
|
|
||||||
|
import AppConstants from '../../constants/application_constants';
|
||||||
|
|
||||||
|
import { escapeHTML } from '../../utils/general_utils';
|
||||||
|
import { InjectInHeadUtils } from '../../utils/inject_utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the component that implements display-specific functionality.
|
* This is the component that implements display-specific functionality.
|
||||||
@ -54,15 +55,13 @@ let Image = React.createClass({
|
|||||||
preview: React.PropTypes.string.isRequired
|
preview: React.PropTypes.string.isRequired
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [InjectInHeadMixin],
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
if(this.props.url) {
|
if(this.props.url) {
|
||||||
this.inject('https://code.jquery.com/jquery-2.1.4.min.js')
|
InjectInHeadUtils.inject(AppConstants.jquery.sdkUrl)
|
||||||
.then(() =>
|
.then(() =>
|
||||||
Q.all([
|
Q.all([
|
||||||
this.inject(AppConstants.baseUrl + 'static/thirdparty/shmui/shmui.css'),
|
InjectInHeadUtils.inject(AppConstants.shmui.cssUrl),
|
||||||
this.inject(AppConstants.baseUrl + 'static/thirdparty/shmui/jquery.shmui.js')
|
InjectInHeadUtils.inject(AppConstants.shmui.sdkUrl)
|
||||||
]).then(() => { window.jQuery('.shmui-ascribe').shmui(); }));
|
]).then(() => { window.jQuery('.shmui-ascribe').shmui(); }));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -87,10 +86,8 @@ let Audio = React.createClass({
|
|||||||
url: React.PropTypes.string.isRequired
|
url: React.PropTypes.string.isRequired
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [InjectInHeadMixin],
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.inject(AppConstants.baseUrl + 'static/thirdparty/audiojs/audiojs/audio.min.js').then(this.ready);
|
InjectInHeadUtils.inject(AppConstants.audiojs.sdkUrl).then(this.ready);
|
||||||
},
|
},
|
||||||
|
|
||||||
ready() {
|
ready() {
|
||||||
@ -121,7 +118,7 @@ let Video = React.createClass({
|
|||||||
* `false` if we failed to load the external library)
|
* `false` if we failed to load the external library)
|
||||||
* 2) render the cover using the `<Image />` component (because libraryLoaded is null)
|
* 2) render the cover using the `<Image />` component (because libraryLoaded is null)
|
||||||
* 3) on `componentDidMount`, we load the external `css` and `js` resources using
|
* 3) on `componentDidMount`, we load the external `css` and `js` resources using
|
||||||
* the `InjectInHeadMixin`, attaching a function to `Promise.then` to change
|
* the `InjectInHeadUtils`, attaching a function to `Promise.then` to change
|
||||||
* `state.libraryLoaded` to true
|
* `state.libraryLoaded` to true
|
||||||
* 4) when the promise is succesfully resolved, we change `state.libraryLoaded` triggering
|
* 4) when the promise is succesfully resolved, we change `state.libraryLoaded` triggering
|
||||||
* a re-render
|
* a re-render
|
||||||
@ -139,20 +136,22 @@ let Video = React.createClass({
|
|||||||
encodingStatus: React.PropTypes.number
|
encodingStatus: React.PropTypes.number
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [InjectInHeadMixin],
|
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return { libraryLoaded: null, videoMounted: false };
|
return { libraryLoaded: null, videoMounted: false };
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
Q.all([
|
Q.all([
|
||||||
this.inject('//vjs.zencdn.net/4.12/video-js.css'),
|
InjectInHeadUtils.inject(AppConstants.videojs.cssUrl),
|
||||||
this.inject('//vjs.zencdn.net/4.12/video.js')])
|
InjectInHeadUtils.inject(AppConstants.videojs.sdkUrl)])
|
||||||
.then(() => this.setState({libraryLoaded: true}))
|
.then(() => this.setState({libraryLoaded: true}))
|
||||||
.fail(() => this.setState({libraryLoaded: false}));
|
.fail(() => this.setState({libraryLoaded: false}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
|
return nextState.videoMounted === false;
|
||||||
|
},
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
if (this.state.libraryLoaded && !this.state.videoMounted) {
|
if (this.state.libraryLoaded && !this.state.videoMounted) {
|
||||||
window.videojs('#mainvideo');
|
window.videojs('#mainvideo');
|
||||||
@ -178,10 +177,6 @@ let Video = React.createClass({
|
|||||||
return html.join('\n');
|
return html.join('\n');
|
||||||
},
|
},
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
|
||||||
return nextState.videoMounted === false;
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.state.libraryLoaded !== null) {
|
if (this.state.libraryLoaded !== null) {
|
||||||
return (
|
return (
|
||||||
|
@ -49,7 +49,11 @@ export default function AuthProxyHandler({to, when}) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
this.redirectConditionally();
|
// Only refresh this component, when UserSources are not loading
|
||||||
|
// data from the server
|
||||||
|
if(!UserStore.isLoading()) {
|
||||||
|
this.redirectConditionally();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -27,7 +27,7 @@ let AccountSettings = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleSuccess(){
|
handleSuccess(){
|
||||||
this.props.loadUser();
|
this.props.loadUser(true);
|
||||||
let notification = new GlobalNotificationModel(getLangText('Settings succesfully updated'), 'success', 5000);
|
let notification = new GlobalNotificationModel(getLangText('Settings succesfully updated'), 'success', 5000);
|
||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
},
|
},
|
||||||
|
@ -46,8 +46,8 @@ let SettingsContainer = React.createClass({
|
|||||||
UserStore.unlisten(this.onChange);
|
UserStore.unlisten(this.onChange);
|
||||||
},
|
},
|
||||||
|
|
||||||
loadUser(){
|
loadUser(invalidateCache){
|
||||||
UserActions.fetchCurrentUser();
|
UserActions.fetchCurrentUser(invalidateCache);
|
||||||
},
|
},
|
||||||
|
|
||||||
onChange(state) {
|
onChange(state) {
|
||||||
|
51
js/components/ascribe_social_share/facebook_share_button.js
Normal file
51
js/components/ascribe_social_share/facebook_share_button.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import AppConstants from '../../constants/application_constants';
|
||||||
|
|
||||||
|
import { InjectInHeadUtils } from '../../utils/inject_utils';
|
||||||
|
|
||||||
|
let FacebookShareButton = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
type: React.PropTypes.string
|
||||||
|
},
|
||||||
|
|
||||||
|
getDefaultProps() {
|
||||||
|
return {
|
||||||
|
type: 'button'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
/**
|
||||||
|
* Ideally we would only use FB.XFBML.parse() on the component that we're
|
||||||
|
* mounting, but doing this when we first load the FB sdk causes unpredictable behaviour.
|
||||||
|
* The button sometimes doesn't get initialized, likely because FB hasn't properly
|
||||||
|
* been initialized yet.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
InjectInHeadUtils
|
||||||
|
.inject(AppConstants.facebook.sdkUrl)
|
||||||
|
.then(() => { FB.XFBML.parse(this.refs.fbShareButton.getDOMNode().parentElement) });
|
||||||
|
},
|
||||||
|
|
||||||
|
shouldComponentUpdate(nextProps) {
|
||||||
|
return this.props.type !== nextProps.type;
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
ref="fbShareButton"
|
||||||
|
className="fb-share-button btn btn-ascribe-social"
|
||||||
|
data-layout={this.props.type}>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default FacebookShareButton;
|
55
js/components/ascribe_social_share/twitter_share_button.js
Normal file
55
js/components/ascribe_social_share/twitter_share_button.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import AppConstants from '../../constants/application_constants';
|
||||||
|
|
||||||
|
import { InjectInHeadUtils } from '../../utils/inject_utils';
|
||||||
|
|
||||||
|
let TwitterShareButton = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
count: React.PropTypes.string,
|
||||||
|
counturl: React.PropTypes.string,
|
||||||
|
hashtags: React.PropTypes.string,
|
||||||
|
size: React.PropTypes.string,
|
||||||
|
text: React.PropTypes.string,
|
||||||
|
url: React.PropTypes.string,
|
||||||
|
via: React.PropTypes.string
|
||||||
|
},
|
||||||
|
|
||||||
|
getDefaultProps() {
|
||||||
|
return {
|
||||||
|
count: 'none',
|
||||||
|
via: 'ascribeIO'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
InjectInHeadUtils.inject(AppConstants.twitter.sdkUrl).then(this.loadTwitterButton);
|
||||||
|
},
|
||||||
|
|
||||||
|
loadTwitterButton() {
|
||||||
|
const { count, counturl, hashtags, size, text, url, via } = this.props;
|
||||||
|
|
||||||
|
twttr.widgets.createShareButton(url, this.refs.twitterShareButton.getDOMNode(), {
|
||||||
|
count,
|
||||||
|
counturl,
|
||||||
|
hashtags,
|
||||||
|
size,
|
||||||
|
text,
|
||||||
|
via,
|
||||||
|
dnt: true // Do not track
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
ref="twitterShareButton"
|
||||||
|
className="btn btn-ascribe-social">
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default TwitterShareButton;
|
@ -58,8 +58,14 @@ let FileDragAndDropDialog = React.createClass({
|
|||||||
return (
|
return (
|
||||||
<div className="file-drag-and-drop-dialog present-options">
|
<div className="file-drag-and-drop-dialog present-options">
|
||||||
<p>{getLangText('Would you rather')}</p>
|
<p>{getLangText('Would you rather')}</p>
|
||||||
|
{/*
|
||||||
|
The frontend in live is hosted under /app,
|
||||||
|
Since `Link` is appending that base url, if its defined
|
||||||
|
by itself, we need to make sure to not set it at this point.
|
||||||
|
Otherwise it will be appended twice.
|
||||||
|
*/}
|
||||||
<Link
|
<Link
|
||||||
to={window.location.pathname}
|
to={`/${window.location.pathname.split('/').pop()}`}
|
||||||
query={queryParamsHash}>
|
query={queryParamsHash}>
|
||||||
<span className="btn btn-default btn-sm">
|
<span className="btn btn-default btn-sm">
|
||||||
{getLangText('Hash your work')}
|
{getLangText('Hash your work')}
|
||||||
@ -69,7 +75,7 @@ let FileDragAndDropDialog = React.createClass({
|
|||||||
<span> or </span>
|
<span> or </span>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
to={window.location.pathname}
|
to={`/${window.location.pathname.split('/').pop()}`}
|
||||||
query={queryParamsUpload}>
|
query={queryParamsUpload}>
|
||||||
<span className="btn btn-default btn-sm">
|
<span className="btn btn-default btn-sm">
|
||||||
{getLangText('Upload and hash your work')}
|
{getLangText('Upload and hash your work')}
|
||||||
|
@ -11,7 +11,7 @@ let Footer = React.createClass({
|
|||||||
<p className="ascribe-sub-sub-statement">
|
<p className="ascribe-sub-sub-statement">
|
||||||
<br />
|
<br />
|
||||||
<a href="http://docs.ascribe.apiary.io/" target="_blank">api</a> |
|
<a href="http://docs.ascribe.apiary.io/" target="_blank">api</a> |
|
||||||
<a href="https://www.ascribe.io/impressum/" target="_blank"> impressum</a> |
|
<a href="https://www.ascribe.io/imprint/" target="_blank"> {getLangText('imprint')}</a> |
|
||||||
<a href="https://www.ascribe.io/terms/" target="_blank"> {getLangText('terms of service')}</a> |
|
<a href="https://www.ascribe.io/terms/" target="_blank"> {getLangText('terms of service')}</a> |
|
||||||
<a href="https://www.ascribe.io/privacy/" target="_blank"> {getLangText('privacy')}</a>
|
<a href="https://www.ascribe.io/privacy/" target="_blank"> {getLangText('privacy')}</a>
|
||||||
</p>
|
</p>
|
||||||
|
@ -71,7 +71,7 @@ const ROUTES = {
|
|||||||
component={AuthProxyHandler({to: '/register_piece', when: 'loggedIn'})(SPLoginContainer)} />
|
component={AuthProxyHandler({to: '/register_piece', when: 'loggedIn'})(SPLoginContainer)} />
|
||||||
<Route
|
<Route
|
||||||
path='logout'
|
path='logout'
|
||||||
component={AuthProxyHandler({to: '/', when: 'loggedOut'})(LogoutContainer)}/>
|
component={AuthProxyHandler({to: '/', when: 'loggedOut'})(LogoutContainer)} />
|
||||||
<Route
|
<Route
|
||||||
path='signup'
|
path='signup'
|
||||||
component={AuthProxyHandler({to: '/register_piece', when: 'loggedIn'})(SPSignupContainer)} />
|
component={AuthProxyHandler({to: '/register_piece', when: 'loggedIn'})(SPSignupContainer)} />
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
export const InformationTexts = {
|
export const AclInformationText = {
|
||||||
'titles': {
|
'titles': {
|
||||||
'acl_consign': 'CONSIGN',
|
'acl_consign': 'CONSIGN',
|
||||||
'acl_loan': 'LOAN',
|
'acl_loan': 'LOAN',
|
||||||
'acl_share': 'SHARE',
|
'acl_loan_request': 'LOAN',
|
||||||
|
'acl_share': 'EMAIL',
|
||||||
|
'acl_transfer': 'TRANSFER',
|
||||||
'acl_delete': 'DELETE',
|
'acl_delete': 'DELETE',
|
||||||
'acl_create_editions': 'CREATE EDITIONS',
|
'acl_create_editions': 'CREATE EDITIONS',
|
||||||
'acl_unconsign': 'UNCONSIGN',
|
'acl_unconsign': 'UNCONSIGN',
|
||||||
@ -13,7 +15,8 @@ export const InformationTexts = {
|
|||||||
'informationSentences': {
|
'informationSentences': {
|
||||||
'acl_consign': ' - Lets someone represent you in dealing with the work, under the terms you agree to.',
|
'acl_consign': ' - Lets someone represent you in dealing with the work, under the terms you agree to.',
|
||||||
'acl_loan': ' - Lets someone use or put the Work on display for a limited amount of time.',
|
'acl_loan': ' - Lets someone use or put the Work on display for a limited amount of time.',
|
||||||
'acl_share': ' - Lets someone view the Work or Edition, but does not give rights to publish or display it.',
|
'acl_share': ' - Lets someone view the Work or Edition via email, but does not give rights to publish or display it.',
|
||||||
|
'acl_transfer': ' - Changes ownership of an Edition. As with a physical piece Work, Transferring ownership of an Edition does not transfer copyright in the Work.',
|
||||||
'acl_delete': ' - Removes the Work from your Wallet. Note that the previous registration and transfer ' +
|
'acl_delete': ' - Removes the Work from your Wallet. Note that the previous registration and transfer ' +
|
||||||
'history will still exist on the blockchain and cannot be deleted.',
|
'history will still exist on the blockchain and cannot be deleted.',
|
||||||
'acl_create_editions': ' Lets the artist set a fixed number of editions of a work which can then be transferred, guaranteeing each edition is authentic and from the artist.',
|
'acl_create_editions': ' Lets the artist set a fixed number of editions of a work which can then be transferred, guaranteeing each edition is authentic and from the artist.',
|
||||||
@ -24,7 +27,8 @@ export const InformationTexts = {
|
|||||||
'acl_consign': '(e.g. an artist Consigns 10 Editions of her new Work to a gallery ' +
|
'acl_consign': '(e.g. an artist Consigns 10 Editions of her new Work to a gallery ' +
|
||||||
'so the gallery can sell them on her behalf, under the terms the artist and the gallery have agreed to)',
|
'so the gallery can sell them on her behalf, under the terms the artist and the gallery have agreed to)',
|
||||||
'acl_loan': '(e.g. a collector Loans a Work to a gallery for one month for display in the gallery\'s show)',
|
'acl_loan': '(e.g. a collector Loans a Work to a gallery for one month for display in the gallery\'s show)',
|
||||||
'acl_share': '(e.g. a photographer Shares proofs of a graduation photo with the graduate\'s grandparents)',
|
'acl_share': '(e.g. a photographer Shares proofs of a graduation photo with the graduate\'s grandparents by email)',
|
||||||
|
'acl_transfer': '(e.g. a musician Transfers limited edition 1 of 10 of her new album to a very happy fan)',
|
||||||
'acl_delete': '(e.g. an artist uploaded the wrong file and doesn\'t want it cluttering his Wallet, so he Deletes it)',
|
'acl_delete': '(e.g. an artist uploaded the wrong file and doesn\'t want it cluttering his Wallet, so he Deletes it)',
|
||||||
'acl_create_editions': '(e.g. A company commissions a visual artists to create three limited edition prints for a giveaway)',
|
'acl_create_editions': '(e.g. A company commissions a visual artists to create three limited edition prints for a giveaway)',
|
||||||
'acl_unconsign': '(e.g. An artist regains full control over their work and releases the consignee of any rights or responsibilities)',
|
'acl_unconsign': '(e.g. An artist regains full control over their work and releases the consignee of any rights or responsibilities)',
|
@ -1,15 +1,19 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
let constants = {
|
//const baseUrl = 'http://localhost:8000/api/';
|
||||||
//'baseUrl': 'http://localhost:8000/api/',
|
|
||||||
|
|
||||||
//FIXME: referring to a global variable in `window` is not
|
//FIXME: referring to a global variable in `window` is not
|
||||||
// super pro. What if we render stuff on the server?
|
// super pro. What if we render stuff on the server?
|
||||||
// - super-bro - Senor Developer, 14th July 2015
|
// - super-bro - Senor Developer, 14th July 2015
|
||||||
//'baseUrl': window.BASE_URL,
|
//const baseUrl = window.BASE_URL;
|
||||||
'apiEndpoint': window.API_ENDPOINT,
|
const apiEndpoint = window.API_ENDPOINT;
|
||||||
'serverUrl': window.SERVER_URL,
|
const serverUrl = window.SERVER_URL;
|
||||||
'baseUrl': window.BASE_URL,
|
const baseUrl = window.BASE_URL;
|
||||||
|
|
||||||
|
const constants = {
|
||||||
|
apiEndpoint,
|
||||||
|
serverUrl,
|
||||||
|
baseUrl,
|
||||||
'aclList': ['acl_coa', 'acl_consign', 'acl_delete', 'acl_download', 'acl_edit', 'acl_create_editions', 'acl_view_editions',
|
'aclList': ['acl_coa', 'acl_consign', 'acl_delete', 'acl_download', 'acl_edit', 'acl_create_editions', 'acl_view_editions',
|
||||||
'acl_loan', 'acl_loan_request', 'acl_share', 'acl_transfer', 'acl_unconsign', 'acl_unshare', 'acl_view',
|
'acl_loan', 'acl_loan_request', 'acl_share', 'acl_transfer', 'acl_unconsign', 'acl_unshare', 'acl_view',
|
||||||
'acl_withdraw_transfer', 'acl_wallet_submit'],
|
'acl_withdraw_transfer', 'acl_wallet_submit'],
|
||||||
@ -84,16 +88,40 @@ let constants = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// in case of whitelabel customization, we store stuff here
|
|
||||||
'whitelabel': {},
|
|
||||||
'raven': {
|
|
||||||
'url': 'https://0955da3388c64ab29bd32c2a429f9ef4@app.getsentry.com/48351'
|
|
||||||
},
|
|
||||||
'copyrightAssociations': ['ARS', 'DACS', 'Bildkunst', 'Pictoright', 'SODRAC', 'Copyright Agency/Viscopy', 'SAVA',
|
'copyrightAssociations': ['ARS', 'DACS', 'Bildkunst', 'Pictoright', 'SODRAC', 'Copyright Agency/Viscopy', 'SAVA',
|
||||||
'Bildrecht GmbH', 'SABAM', 'AUTVIS', 'CREAIMAGEN', 'SONECA', 'Copydan', 'EAU', 'Kuvasto', 'GCA', 'HUNGART',
|
'Bildrecht GmbH', 'SABAM', 'AUTVIS', 'CREAIMAGEN', 'SONECA', 'Copydan', 'EAU', 'Kuvasto', 'GCA', 'HUNGART',
|
||||||
'IVARO', 'SIAE', 'JASPAR-SPDA', 'AKKA/LAA', 'LATGA-A', 'SOMAAP', 'ARTEGESTION', 'CARIER', 'BONO', 'APSAV',
|
'IVARO', 'SIAE', 'JASPAR-SPDA', 'AKKA/LAA', 'LATGA-A', 'SOMAAP', 'ARTEGESTION', 'CARIER', 'BONO', 'APSAV',
|
||||||
'SPA', 'GESTOR', 'VISaRTA', 'RAO', 'LITA', 'DALRO', 'VeGaP', 'BUS', 'ProLitteris', 'AGADU', 'AUTORARTE', 'BUBEDRA', 'BBDA', 'BCDA', 'BURIDA', 'ADAVIS', 'BSDA'],
|
'SPA', 'GESTOR', 'VISaRTA', 'RAO', 'LITA', 'DALRO', 'VeGaP', 'BUS', 'ProLitteris', 'AGADU', 'AUTORARTE', 'BUBEDRA', 'BBDA', 'BCDA', 'BURIDA', 'ADAVIS', 'BSDA'],
|
||||||
'searchThreshold': 500
|
'searchThreshold': 500,
|
||||||
|
|
||||||
|
// in case of whitelabel customization, we store stuff here
|
||||||
|
'whitelabel': {},
|
||||||
|
|
||||||
|
// 3rd party integrations
|
||||||
|
'jquery': {
|
||||||
|
'sdkUrl': 'https://code.jquery.com/jquery-2.1.4.min.js'
|
||||||
|
},
|
||||||
|
'shmui': {
|
||||||
|
'sdkUrl': baseUrl + 'static/thirdparty/shmui/jquery.shmui.js',
|
||||||
|
'cssUrl': baseUrl + 'static/thirdparty/shmui/shmui.css'
|
||||||
|
},
|
||||||
|
'audiojs': {
|
||||||
|
'sdkUrl': baseUrl + 'static/thirdparty/audiojs/audiojs/audio.min.js'
|
||||||
|
},
|
||||||
|
'videojs': {
|
||||||
|
'sdkUrl': '//vjs.zencdn.net/4.12/video.js',
|
||||||
|
'cssUrl': '//vjs.zencdn.net/4.12/video-js.css'
|
||||||
|
},
|
||||||
|
'raven': {
|
||||||
|
'url': 'https://0955da3388c64ab29bd32c2a429f9ef4@app.getsentry.com/48351'
|
||||||
|
},
|
||||||
|
'facebook': {
|
||||||
|
'appId': '420813844732240',
|
||||||
|
'sdkUrl': '//connect.facebook.net/en_US/sdk.js'
|
||||||
|
},
|
||||||
|
'twitter': {
|
||||||
|
'sdkUrl': 'https://platform.twitter.com/widgets.js'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default constants;
|
export default constants;
|
||||||
|
@ -17,6 +17,7 @@ const languages = {
|
|||||||
'%s license': '%s license',
|
'%s license': '%s license',
|
||||||
'Log into': 'Log into',
|
'Log into': 'Log into',
|
||||||
'Email': 'Email',
|
'Email': 'Email',
|
||||||
|
'EMAIL': 'EMAIL',
|
||||||
'Enter your email': 'Enter your email',
|
'Enter your email': 'Enter your email',
|
||||||
'Password': 'Password',
|
'Password': 'Password',
|
||||||
'Enter your password': 'Enter your password',
|
'Enter your password': 'Enter your password',
|
||||||
@ -245,6 +246,7 @@ const languages = {
|
|||||||
'%s license': '%s license',
|
'%s license': '%s license',
|
||||||
'Log into': 'Log into',
|
'Log into': 'Log into',
|
||||||
'Email': 'Email',
|
'Email': 'Email',
|
||||||
|
'EMAIL': 'EMAIL',
|
||||||
'Enter your email': 'Enter your email',
|
'Enter your email': 'Enter your email',
|
||||||
'Password': 'Password',
|
'Password': 'Password',
|
||||||
'Enter your password': 'Enter your password',
|
'Enter your password': 'Enter your password',
|
||||||
@ -473,6 +475,7 @@ const languages = {
|
|||||||
'%s license': '%s license',
|
'%s license': '%s license',
|
||||||
'Log into': 'Se connecter à',
|
'Log into': 'Se connecter à',
|
||||||
'Email': 'E-mail',
|
'Email': 'E-mail',
|
||||||
|
'EMAIL': 'E-MAIL',
|
||||||
'Enter your email': 'Entrez votre courriel',
|
'Enter your email': 'Entrez votre courriel',
|
||||||
'Password': 'Mot de passe',
|
'Password': 'Mot de passe',
|
||||||
'Enter your password': 'Entrez votre mot de passe',
|
'Enter your password': 'Entrez votre mot de passe',
|
||||||
|
9
js/fetchers/README.md
Normal file
9
js/fetchers/README.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# README
|
||||||
|
|
||||||
|
Recently, we've been discovering [alt.js's sources](http://alt.js.org/docs/async/). As it allows us to implement caching as well as avoid a lot of unnecessary boilerplate code, we do not want to write any more NEW `fetcher`s.
|
||||||
|
|
||||||
|
So if you need to query an endpoint, or if you make active changes to one of the `fetcher`s, please consider refactoring it to a `source`.
|
||||||
|
|
||||||
|
Also, please read the `NAMING_CONVENTIONS.md` file in there first.
|
||||||
|
|
||||||
|
Thanks
|
@ -1,20 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import requests from '../utils/requests';
|
|
||||||
import ApiUrls from '../constants/api_urls';
|
|
||||||
|
|
||||||
let UserFetcher = {
|
|
||||||
/**
|
|
||||||
* Fetch one user from the API.
|
|
||||||
* If no arg is supplied, load the current user
|
|
||||||
*/
|
|
||||||
fetchOne() {
|
|
||||||
return requests.get('user');
|
|
||||||
},
|
|
||||||
|
|
||||||
logout() {
|
|
||||||
return requests.get(ApiUrls.users_logout);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default UserFetcher;
|
|
@ -1,17 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import requests from '../utils/requests';
|
|
||||||
|
|
||||||
import { getSubdomain } from '../utils/general_utils';
|
|
||||||
|
|
||||||
|
|
||||||
let WhitelabelFetcher = {
|
|
||||||
/**
|
|
||||||
* Fetch the custom whitelabel data from the API.
|
|
||||||
*/
|
|
||||||
fetch() {
|
|
||||||
return requests.get('whitelabel_settings', {'subdomain': getSubdomain()});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default WhitelabelFetcher;
|
|
@ -1,71 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import Q from 'q';
|
|
||||||
|
|
||||||
let mapAttr = {
|
|
||||||
link: 'href',
|
|
||||||
script: 'src'
|
|
||||||
};
|
|
||||||
|
|
||||||
let mapTag = {
|
|
||||||
js: 'script',
|
|
||||||
css: 'link'
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
let InjectInHeadMixin = {
|
|
||||||
/**
|
|
||||||
* Provide functions to inject `<script>` and `<link>` in `<head>`.
|
|
||||||
* Useful when you have to load a huge external library and
|
|
||||||
* you don't want to embed everything inside the build file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
isPresent(tag, src) {
|
|
||||||
let attr = mapAttr[tag];
|
|
||||||
let query = `head > ${tag}[${attr}="${src}"]`;
|
|
||||||
return document.querySelector(query);
|
|
||||||
},
|
|
||||||
|
|
||||||
injectTag(tag, src) {
|
|
||||||
return Q.Promise((resolve, reject) => {
|
|
||||||
if (InjectInHeadMixin.isPresent(tag, src)) {
|
|
||||||
resolve();
|
|
||||||
} else {
|
|
||||||
let attr = mapAttr[tag];
|
|
||||||
let element = document.createElement(tag);
|
|
||||||
if (tag === 'script') {
|
|
||||||
element.onload = () => resolve();
|
|
||||||
element.onerror = () => reject();
|
|
||||||
} else {
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
document.head.appendChild(element);
|
|
||||||
element[attr] = src;
|
|
||||||
if (tag === 'link') {
|
|
||||||
element.rel = 'stylesheet';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
injectStylesheet(src) {
|
|
||||||
return InjectInHeadMixin.injectTag('link', src);
|
|
||||||
},
|
|
||||||
|
|
||||||
injectScript(src) {
|
|
||||||
return InjectInHeadMixin.injectTag('source', src);
|
|
||||||
},
|
|
||||||
|
|
||||||
inject(src) {
|
|
||||||
let ext = src.split('.').pop();
|
|
||||||
let tag = mapTag[ext];
|
|
||||||
if (!tag) {
|
|
||||||
throw new Error(`Cannot inject ${src} in the DOM, cannot guess the tag name from extension "${ext}". Valid extensions are "js" and "css".`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return InjectInHeadMixin.injectTag(tag, src);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
export default InjectInHeadMixin;
|
|
70
js/sources/NAMING_CONVENTIONS.md
Normal file
70
js/sources/NAMING_CONVENTIONS.md
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# Naming conventions for sources
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
When using [alt.js's sources](http://alt.js.org/docs/async/), we don't want the source's method to clash with the store/action's method names.
|
||||||
|
|
||||||
|
While actions will still be named by the following schema:
|
||||||
|
|
||||||
|
```
|
||||||
|
<verb><ObjectToManipulateInTheStore>
|
||||||
|
```
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
|
||||||
|
```
|
||||||
|
fetchCurrentUser
|
||||||
|
logoutCurrentUser
|
||||||
|
fetchApplication
|
||||||
|
refreshApplicationToken
|
||||||
|
```
|
||||||
|
|
||||||
|
we cannot repeat this for a sources' methods as patterns like this would emerge in the stores:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
onFetchCurrentUser(invalidateCache) {
|
||||||
|
this.invalidateCache = invalidateCache;
|
||||||
|
|
||||||
|
if(!this.getInstance().isLoading()) {
|
||||||
|
this.getInstance().fetchCurrentUser(); // does not call a flux "action" but a method in user_source.js - which is confusing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Therefore we're introducing the following naming convention:
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
1. All source methods that perform a data lookup of any kind (be it cached or not), are called `lookup<ObjectToManipulateInTheStore>`. As a mnemonic aid, "You *lookup* something in a *source*".
|
||||||
|
2. Promise-based callback methods - provided by alt.js - prepend their action, followed by `ObjectToManipulateInTheStore` (e.g. `error<ObjectToManipulateInTheStore>`)
|
||||||
|
2. For all methods that do not fit 1.), we prepend `perform`.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Examples for Rule 1.)
|
||||||
|
*HTTP GET'ing the current User*
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
UserActions.fetchCurrentUser
|
||||||
|
UserStore.onFetchCurrentUser
|
||||||
|
UserSource.lookupCurrentUser
|
||||||
|
```
|
||||||
|
|
||||||
|
### Examples for Rule 2.)
|
||||||
|
This talks about the naming in an actual `*_source.js` file:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
lookupCurrentUser: {
|
||||||
|
success: UserActions.successFetchCurrentUser, // 'success<ObjectToManipulateInTheStore>'
|
||||||
|
error: UserActions.errorCurrentUser, // 'error<ObjectToManipulateInTheStore>'
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
### Examples for Rule 3.)
|
||||||
|
*HTTP GET'ing a certain user endpoint, that logs the user out :sad_face:(, as this should not be a GET request anyway)*
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
UserActions.logoutCurrentUser
|
||||||
|
UserStore.onLogoutCurrentUser
|
||||||
|
UserSource.performLogoutCurrentUser
|
||||||
|
```
|
34
js/sources/user_source.js
Normal file
34
js/sources/user_source.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import requests from '../utils/requests';
|
||||||
|
import ApiUrls from '../constants/api_urls';
|
||||||
|
|
||||||
|
import UserActions from '../actions/user_actions';
|
||||||
|
|
||||||
|
|
||||||
|
const UserSource = {
|
||||||
|
lookupCurrentUser: {
|
||||||
|
remote() {
|
||||||
|
return requests.get('user');
|
||||||
|
},
|
||||||
|
|
||||||
|
local(state) {
|
||||||
|
return state.currentUser && state.currentUser.email ? state : {};
|
||||||
|
},
|
||||||
|
success: UserActions.successFetchCurrentUser,
|
||||||
|
error: UserActions.errorCurrentUser,
|
||||||
|
shouldFetch(state) {
|
||||||
|
return state.userMeta.invalidateCache || state.currentUser && !state.currentUser.email;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
performLogoutCurrentUser: {
|
||||||
|
remote() {
|
||||||
|
return requests.get(ApiUrls.users_logout);
|
||||||
|
},
|
||||||
|
success: UserActions.successLogoutCurrentUser,
|
||||||
|
error: UserActions.errorCurrentUser
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UserSource;
|
25
js/sources/whitelabel_source.js
Normal file
25
js/sources/whitelabel_source.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import requests from '../utils/requests';
|
||||||
|
import WhitelabelActions from '../actions/whitelabel_actions';
|
||||||
|
|
||||||
|
import { getSubdomain } from '../utils/general_utils';
|
||||||
|
|
||||||
|
|
||||||
|
const WhitelabelSource = {
|
||||||
|
lookupWhitelabel: {
|
||||||
|
remote() {
|
||||||
|
return requests.get('whitelabel_settings', {'subdomain': getSubdomain()});
|
||||||
|
},
|
||||||
|
local(state) {
|
||||||
|
return Object.keys(state.whitelabel).length > 0 ? state : {};
|
||||||
|
},
|
||||||
|
success: WhitelabelActions.successFetchWhitelabel,
|
||||||
|
error: WhitelabelActions.errorWhitelabel,
|
||||||
|
shouldFetch(state) {
|
||||||
|
return state.whitelabelMeta.invalidateCache || Object.keys(state.whitelabel).length === 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WhitelabelSource;
|
@ -3,19 +3,47 @@
|
|||||||
import { altUser } from '../alt';
|
import { altUser } from '../alt';
|
||||||
import UserActions from '../actions/user_actions';
|
import UserActions from '../actions/user_actions';
|
||||||
|
|
||||||
|
import UserSource from '../sources/user_source';
|
||||||
|
|
||||||
|
|
||||||
class UserStore {
|
class UserStore {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.currentUser = {};
|
this.currentUser = {};
|
||||||
|
this.userMeta = {
|
||||||
|
invalidateCache: false,
|
||||||
|
err: null
|
||||||
|
};
|
||||||
|
|
||||||
this.bindActions(UserActions);
|
this.bindActions(UserActions);
|
||||||
|
this.registerAsync(UserSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpdateCurrentUser(user) {
|
onFetchCurrentUser(invalidateCache) {
|
||||||
|
this.userMeta.invalidateCache = invalidateCache;
|
||||||
|
|
||||||
|
if(!this.getInstance().isLoading()) {
|
||||||
|
this.getInstance().lookupCurrentUser();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onSuccessFetchCurrentUser({users: [user]}) {
|
||||||
|
this.userMeta.invalidateCache = false;
|
||||||
|
this.userMeta.err = null;
|
||||||
this.currentUser = user;
|
this.currentUser = user;
|
||||||
}
|
}
|
||||||
onDeleteCurrentUser() {
|
|
||||||
|
onLogoutCurrentUser() {
|
||||||
|
this.getInstance().performLogoutCurrentUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
onSuccessLogoutCurrentUser() {
|
||||||
this.currentUser = {};
|
this.currentUser = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onErrorCurrentUser(err) {
|
||||||
|
console.logGlobal(err);
|
||||||
|
this.userMeta.err = err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default altUser.createStore(UserStore, 'UserStore');
|
export default altUser.createStore(UserStore, 'UserStore');
|
||||||
|
@ -2,17 +2,39 @@
|
|||||||
|
|
||||||
import { altWhitelabel } from '../alt';
|
import { altWhitelabel } from '../alt';
|
||||||
import WhitelabelActions from '../actions/whitelabel_actions';
|
import WhitelabelActions from '../actions/whitelabel_actions';
|
||||||
|
import WhitelabelSource from '../sources/whitelabel_source';
|
||||||
|
|
||||||
|
|
||||||
class WhitelabelStore {
|
class WhitelabelStore {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.whitelabel = {};
|
this.whitelabel = {};
|
||||||
|
this.whitelabelMeta = {
|
||||||
|
invalidateCache: false,
|
||||||
|
err: null
|
||||||
|
};
|
||||||
|
|
||||||
this.bindActions(WhitelabelActions);
|
this.bindActions(WhitelabelActions);
|
||||||
|
this.registerAsync(WhitelabelSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpdateWhitelabel(whitelabel) {
|
onFetchWhitelabel(invalidateCache) {
|
||||||
|
this.whitelabelMeta.invalidateCache = invalidateCache;
|
||||||
|
|
||||||
|
if(!this.getInstance().isLoading()) {
|
||||||
|
this.getInstance().lookupWhitelabel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onSuccessFetchWhitelabel({ whitelabel }) {
|
||||||
|
this.whitelabelMeta.invalidateCache = false;
|
||||||
|
this.whitelabelMeta.err = null;
|
||||||
this.whitelabel = whitelabel;
|
this.whitelabel = whitelabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onErrorCurrentUser(err) {
|
||||||
|
console.logGlobal(err);
|
||||||
|
this.whitelabelMeta.err = err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default altWhitelabel.createStore(WhitelabelStore, 'WhitelabelStore');
|
export default altWhitelabel.createStore(WhitelabelStore, 'WhitelabelStore');
|
||||||
|
30
js/third_party/facebook.js
vendored
Normal file
30
js/third_party/facebook.js
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import { altThirdParty } from '../alt';
|
||||||
|
import EventActions from '../actions/event_actions';
|
||||||
|
|
||||||
|
import AppConstants from '../constants/application_constants'
|
||||||
|
|
||||||
|
class FacebookHandler {
|
||||||
|
constructor() {
|
||||||
|
this.bindActions(EventActions);
|
||||||
|
}
|
||||||
|
|
||||||
|
onApplicationWillBoot(settings) {
|
||||||
|
// Callback function that FB's sdk will call when it's finished loading
|
||||||
|
// See https://developers.facebook.com/docs/javascript/quickstart/v2.5
|
||||||
|
window.fbAsyncInit = () => {
|
||||||
|
FB.init({
|
||||||
|
appId: AppConstants.facebook.appId,
|
||||||
|
// Force FB to parse everything on first load to make sure all the XFBML components are initialized.
|
||||||
|
// If we don't do this, we can run into issues with components on the first load who are not be
|
||||||
|
// initialized.
|
||||||
|
xfbml: true,
|
||||||
|
version: 'v2.5',
|
||||||
|
cookie: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default altThirdParty.createStore(FacebookHandler, 'FacebookHandler');
|
67
js/utils/inject_utils.js
Normal file
67
js/utils/inject_utils.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import Q from 'q';
|
||||||
|
|
||||||
|
let mapAttr = {
|
||||||
|
link: 'href',
|
||||||
|
script: 'src'
|
||||||
|
};
|
||||||
|
|
||||||
|
let mapTag = {
|
||||||
|
js: 'script',
|
||||||
|
css: 'link'
|
||||||
|
};
|
||||||
|
|
||||||
|
let tags = {};
|
||||||
|
|
||||||
|
function injectTag(tag, src) {
|
||||||
|
if(!tags[src]) {
|
||||||
|
tags[src] = Q.Promise((resolve, reject) => {
|
||||||
|
let attr = mapAttr[tag];
|
||||||
|
let element = document.createElement(tag);
|
||||||
|
if (tag === 'script') {
|
||||||
|
element.onload = resolve;
|
||||||
|
element.onerror = reject;
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
document.head.appendChild(element);
|
||||||
|
element[attr] = src;
|
||||||
|
if (tag === 'link') {
|
||||||
|
element.rel = 'stylesheet';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return tags[src];
|
||||||
|
}
|
||||||
|
|
||||||
|
function injectStylesheet(src) {
|
||||||
|
return injectTag('link', src);
|
||||||
|
}
|
||||||
|
|
||||||
|
function injectScript(src) {
|
||||||
|
return injectTag('source', src);
|
||||||
|
}
|
||||||
|
|
||||||
|
function inject(src) {
|
||||||
|
let ext = src.split('.').pop();
|
||||||
|
let tag = mapTag[ext];
|
||||||
|
if (!tag) {
|
||||||
|
throw new Error(`Cannot inject ${src} in the DOM, cannot guess the tag name from extension "${ext}". Valid extensions are "js" and "css".`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return injectTag(tag, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const InjectInHeadUtils = {
|
||||||
|
/**
|
||||||
|
* Provide functions to inject `<script>` and `<link>` in `<head>`.
|
||||||
|
* Useful when you have to load a huge external library and
|
||||||
|
* you don't want to embed everything inside the build file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
injectStylesheet,
|
||||||
|
injectScript,
|
||||||
|
inject
|
||||||
|
};
|
10
sass/ascribe_social_share.scss
Normal file
10
sass/ascribe_social_share.scss
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
.ascribe-social-button-list {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-ascribe-social {
|
||||||
|
height: 20px;
|
||||||
|
width: 56px;
|
||||||
|
box-sizing: content-box; /* We want to ignore padding in these calculations as we use the sdk buttons' height and width */
|
||||||
|
padding: 1px 0;
|
||||||
|
}
|
@ -31,6 +31,7 @@ $BASE_URL: '<%= BASE_URL %>';
|
|||||||
@import 'offset_right';
|
@import 'offset_right';
|
||||||
@import 'ascribe_settings';
|
@import 'ascribe_settings';
|
||||||
@import 'ascribe_slides_container';
|
@import 'ascribe_slides_container';
|
||||||
|
@import 'ascribe_social_share';
|
||||||
@import 'ascribe_property';
|
@import 'ascribe_property';
|
||||||
@import 'ascribe_form';
|
@import 'ascribe_form';
|
||||||
@import 'ascribe_panel';
|
@import 'ascribe_panel';
|
||||||
|
Loading…
Reference in New Issue
Block a user