diff --git a/docs/refactor-todo.md b/docs/refactor-todo.md index c0329bc2..04da4929 100644 --- a/docs/refactor-todo.md +++ b/docs/refactor-todo.md @@ -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 - 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. +- Convert all fetchers to [alt.js's sources](http://alt.js.org/docs/async/) # Refactor DONE - Refactor forms to generic-declarative form component ✓ diff --git a/js/actions/user_actions.js b/js/actions/user_actions.js index 3780a802..a661b8de 100644 --- a/js/actions/user_actions.js +++ b/js/actions/user_actions.js @@ -1,37 +1,18 @@ 'use strict'; import { altUser } from '../alt'; -import UserFetcher from '../fetchers/user_fetcher'; class UserActions { constructor() { this.generateActions( - 'updateCurrentUser', - 'deleteCurrentUser' + 'fetchCurrentUser', + '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); diff --git a/js/actions/whitelabel_actions.js b/js/actions/whitelabel_actions.js index a1460fb8..3d0e9d41 100644 --- a/js/actions/whitelabel_actions.js +++ b/js/actions/whitelabel_actions.js @@ -1,29 +1,16 @@ 'use strict'; import { altWhitelabel } from '../alt'; -import WhitelabelFetcher from '../fetchers/whitelabel_fetcher'; class WhitelabelActions { constructor() { 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); diff --git a/js/app.js b/js/app.js index c9451e47..520bedbd 100644 --- a/js/app.js +++ b/js/app.js @@ -30,6 +30,7 @@ import GoogleAnalyticsHandler from './third_party/ga'; import RavenHandler from './third_party/raven'; import IntercomHandler from './third_party/intercom'; import NotificationsHandler from './third_party/notifications'; +import FacebookHandler from './third_party/facebook'; /* eslint-enable */ initLogging(); diff --git a/js/components/ascribe_buttons/acl_button_list.js b/js/components/ascribe_buttons/acl_button_list.js index 83495363..35e42c20 100644 --- a/js/components/ascribe_buttons/acl_button_list.js +++ b/js/components/ascribe_buttons/acl_button_list.js @@ -6,9 +6,9 @@ import UserActions from '../../actions/user_actions'; import UserStore from '../../stores/user_store'; import ConsignButton from './acls/consign_button'; +import EmailButton from './acls/email_button'; import LoanButton from './acls/loan_button'; import LoanRequestButton from './acls/loan_request_button'; -import ShareButton from './acls/share_button'; import TransferButton from './acls/transfer_button'; import UnconsignButton from './acls/unconsign_button'; @@ -41,7 +41,7 @@ let AclButtonList = React.createClass({ componentDidMount() { UserStore.listen(this.onChange); - UserActions.fetchCurrentUser(); + UserActions.fetchCurrentUser.defer(); window.addEventListener('resize', this.handleResize); window.dispatchEvent(new Event('resize')); @@ -90,7 +90,7 @@ let AclButtonList = React.createClass({ return (
- { - 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)); + } }); }, diff --git a/js/components/ascribe_buttons/acls/acl_button.js b/js/components/ascribe_buttons/acls/acl_button.js index 614c6687..97f2e173 100644 --- a/js/components/ascribe_buttons/acls/acl_button.js +++ b/js/components/ascribe_buttons/acls/acl_button.js @@ -11,6 +11,8 @@ import ModalWrapper from '../../ascribe_modal/modal_wrapper'; import AppConstants from '../../../constants/application_constants'; +import { AclInformationText } from '../../../constants/acl_information_text'; + export default function ({ action, displayName, title, tooltip }) { if (AppConstants.aclList.indexOf(action) < 0) { @@ -34,12 +36,11 @@ export default function ({ action, displayName, title, tooltip }) { className: React.PropTypes.string }, - // Removes the acl_ prefix and converts to upper case sanitizeAction() { if (this.props.buttonAcceptName) { return this.props.buttonAcceptName; } - return action.split('acl_')[1].toUpperCase(); + return AclInformationText.titles[action]; }, render() { diff --git a/js/components/ascribe_buttons/acls/share_button.js b/js/components/ascribe_buttons/acls/email_button.js similarity index 53% rename from js/components/ascribe_buttons/acls/share_button.js rename to js/components/ascribe_buttons/acls/email_button.js index 83781aed..81317618 100644 --- a/js/components/ascribe_buttons/acls/share_button.js +++ b/js/components/ascribe_buttons/acls/email_button.js @@ -8,7 +8,7 @@ import { getLangText } from '../../../utils/lang_utils'; export default AclButton({ action: 'acl_share', - displayName: 'ShareButton', - title: getLangText('Share artwork'), - tooltip: getLangText('Share the artwork') + displayName: 'EmailButton', + title: getLangText('Share artwork via email'), + tooltip: getLangText("Share the artwork to another user's collection through email") }); diff --git a/js/components/ascribe_collapsible/collapsible_button.js b/js/components/ascribe_collapsible/collapsible_button.js index 6fb39c71..caf89df3 100644 --- a/js/components/ascribe_collapsible/collapsible_button.js +++ b/js/components/ascribe_collapsible/collapsible_button.js @@ -21,13 +21,13 @@ let CollapsibleButton = React.createClass({ this.setState({expanded: !this.state.expanded}); }, render() { - let isVisible = (this.state.expanded) ? '' : 'invisible'; + let isHidden = (this.state.expanded) ? '' : 'hidden'; return ( {this.props.button} -
+
{this.props.panel}
diff --git a/js/components/ascribe_detail/edition_action_panel.js b/js/components/ascribe_detail/edition_action_panel.js index 6bd8108d..07827fcd 100644 --- a/js/components/ascribe_detail/edition_action_panel.js +++ b/js/components/ascribe_detail/edition_action_panel.js @@ -172,7 +172,7 @@ let EditionActionPanel = React.createClass({ editions={[edition]}/> diff --git a/js/components/ascribe_detail/history_iterator.js b/js/components/ascribe_detail/history_iterator.js index 54d11a5b..413aeb21 100644 --- a/js/components/ascribe_detail/history_iterator.js +++ b/js/components/ascribe_detail/history_iterator.js @@ -5,11 +5,33 @@ import React from 'react'; import Form from '../ascribe_forms/form'; import Property from '../ascribe_forms/property'; +import { replaceSubstringAtIndex } from '../../utils/general_utils'; + + let HistoryIterator = React.createClass({ propTypes: { 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 ( + + {historicalEventDescription} + {contractName} + + ); + } 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() { return (
@@ -20,7 +42,7 @@ let HistoryIterator = React.createClass({ key={i} label={ historicalEvent[0] } editable={false}> -
{ historicalEvent[1] }
+
{this.composeHistoryDescription(historicalEvent)}
); })} diff --git a/js/components/ascribe_detail/media_container.js b/js/components/ascribe_detail/media_container.js index d31a205f..c6845a44 100644 --- a/js/components/ascribe_detail/media_container.js +++ b/js/components/ascribe_detail/media_container.js @@ -7,10 +7,18 @@ import Glyphicon from 'react-bootstrap/lib/Glyphicon'; 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 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 = { video: 315, @@ -24,10 +32,17 @@ let MediaContainer = React.createClass({ }, getInitialState() { - return {timerId: null}; + return mergeOptions( + UserStore.getState(), + { + timerId: null + }); }, componentDidMount() { + UserStore.listen(this.onChange); + UserActions.fetchCurrentUser(); + if (!this.props.content.digital_work) { return; } @@ -45,19 +60,32 @@ let MediaContainer = React.createClass({ }, componentWillUnmount() { + UserStore.unlisten(this.onChange); + window.clearInterval(this.state.timerId); }, + onChange(state) { + this.setState(state); + }, + render() { - let thumbnail = this.props.content.thumbnail.thumbnail_sizes && this.props.content.thumbnail.thumbnail_sizes['600x600'] ? - this.props.content.thumbnail.thumbnail_sizes['600x600'] : this.props.content.thumbnail.url_safe; - let mimetype = this.props.content.digital_work.mime; + const { content } = this.props; + // Pieces and editions are joined to the user by a foreign key in the database, so + // 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 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) { - extraData = this.props.content.digital_work.encoding_urls.map(e => { return { url: e.url, type: e.label }; }); + if (content.digital_work.encoding_urls) { + extraData = content.digital_work.encoding_urls.map(e => { return { url: e.url, type: e.label }; }); } if (['video', 'audio'].indexOf(mimetype) > -1) { @@ -73,7 +101,7 @@ let MediaContainer = React.createClass({ panel={
                             {''}
+                                + content.bitcoin_id + '" frameborder="0" allowfullscreen>'}
                         
}/> ); @@ -83,13 +111,19 @@ let MediaContainer = React.createClass({ + encodingStatus={content.digital_work.isEncoding} />

+ + + + +