diff --git a/README.md b/README.md index 6fae0011..270c4a5f 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,11 @@ gulp serve Additionally, to work on the white labeling functionality, you need to edit your `/etc/hosts` file and add: ``` -127.0.0.1 localhost.com -127.0.0.1 cc.localhost.com +127.0.0.1 localhost.com +127.0.0.1 cc.localhost.com +127.0.0.1 cyland.localhost.com +127.0.0.1 ikonotv.localhost.com +127.0.0.1 sluice.localhost.com ``` diff --git a/js/components/ascribe_app.js b/js/components/ascribe_app.js index b4a894a3..789399b0 100644 --- a/js/components/ascribe_app.js +++ b/js/components/ascribe_app.js @@ -6,15 +6,16 @@ import Header from '../components/header'; import Footer from '../components/footer'; import GlobalNotification from './global_notification'; -// let Link = Router.Link; -let RouteHandler = Router.RouteHandler; +import getRoutes from '../routes'; +let RouteHandler = Router.RouteHandler; + let AscribeApp = React.createClass({ render() { return (
-
+
diff --git a/js/components/nav_routes_links.js b/js/components/nav_routes_links.js new file mode 100644 index 00000000..9a266ba8 --- /dev/null +++ b/js/components/nav_routes_links.js @@ -0,0 +1,68 @@ +'use strict'; + +import React from 'react'; + +import Nav from 'react-bootstrap/lib/Nav'; +import DropdownButton from 'react-bootstrap/lib/DropdownButton'; +import MenuItemLink from 'react-router-bootstrap/lib/MenuItemLink'; +import NavItemLink from 'react-router-bootstrap/lib/NavItemLink'; + +import { sanitizeList } from '../utils/general_utils'; + + +let NavRoutesLinks = React.createClass({ + propTypes: { + routes: React.PropTypes.element + }, + + extractLinksFromRoutes(node, i) { + if(!node) { + return; + } + + node = node.props; + + let links = node.children.map((child, j) => { + + // check if this a candidate for a link generation + if(child.props.headerTitle && typeof child.props.headerTitle === 'string') { + + // also check if it is a candidate for generating a dropdown menu + if(child.props.children && child.props.children.length > 0) { + return ( + + {this.extractLinksFromRoutes(child, i++)} + + ); + } else if(i === 1) { + // if the node's child is actually a node of level one (a child of a node), we're + // returning a DropdownButton matching MenuItemLink + return ( + {child.props.headerTitle} + ); + } else if(i === 0) { + return ( + {child.props.headerTitle} + ); + } else { + return null; + } + } else { + return null; + } + }); + + // remove all nulls from the list of generated links + return sanitizeList(links); + }, + + render() { + return ( + + ); + } +}); + +export default NavRoutesLinks; \ No newline at end of file diff --git a/js/components/routes.js b/js/components/routes.js deleted file mode 100644 index 8b230a5a..00000000 --- a/js/components/routes.js +++ /dev/null @@ -1,25 +0,0 @@ -'use strict'; - -import React from 'react'; -import Router from 'react-router'; - -import App from './ascribe_app'; -import AppConstants from '../constants/application_constants'; - -let Route = Router.Route; -let Redirect = Router.Redirect; -let baseUrl = AppConstants.baseUrl; - - -function getRoutes(commonRoutes) { - return ( - - - - {commonRoutes} - - ); -} - - -export default getRoutes; diff --git a/js/components/whitelabel/prize/prize_app.js b/js/components/whitelabel/prize/prize_app.js index dec859fc..29763ab3 100644 --- a/js/components/whitelabel/prize/prize_app.js +++ b/js/components/whitelabel/prize/prize_app.js @@ -7,6 +7,8 @@ import Header from '../../header'; import Footer from '../../footer'; import GlobalNotification from '../../global_notification'; +import getRoutes from './prize_routes'; + let RouteHandler = Router.RouteHandler; let PrizeApp = React.createClass({ @@ -14,10 +16,14 @@ let PrizeApp = React.createClass({ render() { let header = null; + let subdomain = window.location.host.split('.')[0]; + + let ROUTES = getRoutes(null, subdomain); + if (this.isActive('landing') || this.isActive('login') || this.isActive('signup')) { header = ; } else { - header =
; + header =
; } return ( diff --git a/js/components/whitelabel/prize/prize_routes.js b/js/components/whitelabel/prize/prize_routes.js index 1384d26b..a389cb83 100644 --- a/js/components/whitelabel/prize/prize_routes.js +++ b/js/components/whitelabel/prize/prize_routes.js @@ -29,8 +29,8 @@ function getRoutes() { - - + + diff --git a/js/components/whitelabel/wallet/components/cyland/cyland_register_piece.js b/js/components/whitelabel/wallet/components/cyland/cyland_register_piece.js index 8a092bdf..d1363a51 100644 --- a/js/components/whitelabel/wallet/components/cyland/cyland_register_piece.js +++ b/js/components/whitelabel/wallet/components/cyland/cyland_register_piece.js @@ -216,6 +216,8 @@ let CylandRegisterPiece = React.createClass({ gallery="Cyland Archive" startdate={today} enddate={datetimeWhenWeAllWillBeFlyingCoolHoverboardsAndDinosaursWillLiveAgain} + showStartDate={false} + showEndDate={false} showPersonalMessage={false} handleSuccess={this.handleLoanSuccess}> + className="btn-xs pull-right" + handleSuccess={this.handleSubmitSuccess} + piece={this.props.content}/> + + + ); diff --git a/js/components/whitelabel/wallet/components/ikonotv/ascribe_buttons/ikonotv_submit_button.js b/js/components/whitelabel/wallet/components/ikonotv/ascribe_buttons/ikonotv_submit_button.js index d3f4c0e3..40d65a2b 100644 --- a/js/components/whitelabel/wallet/components/ikonotv/ascribe_buttons/ikonotv_submit_button.js +++ b/js/components/whitelabel/wallet/components/ikonotv/ascribe_buttons/ikonotv_submit_button.js @@ -1,10 +1,18 @@ 'use strict'; import React from 'react'; +import Moment from 'moment'; import classNames from 'classnames'; import ModalWrapper from '../../../../../ascribe_modal/modal_wrapper'; +import LoanForm from '../../../../../ascribe_forms/form_loan'; + +import Property from '../../../../../ascribe_forms/property'; +import InputCheckbox from '../../../../../ascribe_forms/input_checkbox'; + +import ApiUrls from '../../../../../../constants/api_urls'; + import { getLangText } from '../../../../../../utils/lang_utils'; let IkonotvSubmitButton = React.createClass({ @@ -17,23 +25,50 @@ let IkonotvSubmitButton = React.createClass({ getSubmitButton() { return ( ); }, render() { + + let today = new Moment(); + let enddate = new Moment(); + enddate.add(1, 'years'); + return ( - + title={getLangText('Loan to IkonoTV archive')}> + + + + + {' ' + getLangText('I agree to the Terms of Service of IkonoTV Archive') + ' '} + ( + {getLangText('read')} + ) + + + + ); } }); -export default IkonotvSubmitButton; \ No newline at end of file +export default IkonotvSubmitButton; diff --git a/js/components/whitelabel/wallet/components/ikonotv/ascribe_detail/ikonotv_piece_container.js b/js/components/whitelabel/wallet/components/ikonotv/ascribe_detail/ikonotv_piece_container.js new file mode 100644 index 00000000..7f094a26 --- /dev/null +++ b/js/components/whitelabel/wallet/components/ikonotv/ascribe_detail/ikonotv_piece_container.js @@ -0,0 +1,176 @@ +'use strict'; + +import React from 'react'; + +import PieceActions from '../../../../../../actions/piece_actions'; +import PieceStore from '../../../../../../stores/piece_store'; + +import PieceListActions from '../../../../../../actions/piece_list_actions'; +import PieceListStore from '../../../../../../stores/piece_list_store'; + +import UserStore from '../../../../../../stores/user_store'; + +import Piece from '../../../../../../components/ascribe_detail/piece'; + +import ListRequestActions from '../../../../../ascribe_forms/list_form_request_actions'; +import AclButtonList from '../../../../../ascribe_buttons/acl_button_list'; +import DeleteButton from '../../../../../ascribe_buttons/delete_button'; + +import CollapsibleParagraph from '../../../../../../components/ascribe_collapsible/collapsible_paragraph'; + +import IkonotvSubmitButton from '../ascribe_buttons/ikonotv_submit_button'; + +import HistoryIterator from '../../../../../ascribe_detail/history_iterator'; + +import DetailProperty from '../../../../../ascribe_detail/detail_property'; + + +import GlobalNotificationModel from '../../../../../../models/global_notification_model'; +import GlobalNotificationActions from '../../../../../../actions/global_notification_actions'; + +import AclProxy from '../../../../../acl_proxy'; + +import AppConstants from '../../../../../../constants/application_constants'; + +import { getLangText } from '../../../../../../utils/lang_utils'; +import { mergeOptions } from '../../../../../../utils/general_utils'; + + +let IkonotvPieceContainer = React.createClass({ + getInitialState() { + return mergeOptions( + PieceStore.getState(), + UserStore.getState(), + PieceListStore.getState() + ); + }, + + componentDidMount() { + PieceStore.listen(this.onChange); + PieceActions.fetchOne(this.props.params.pieceId); + UserStore.listen(this.onChange); + PieceListStore.listen(this.onChange); + }, + + componentWillReceiveProps(nextProps) { + if(this.props.params.pieceId !== nextProps.params.pieceId) { + PieceActions.updatePiece({}); + PieceActions.fetchOne(nextProps.params.pieceId); + } + }, + + componentWillUnmount() { + // Every time we're leaving the piece detail page, + // just reset the piece that is saved in the piece store + // as it will otherwise display wrong/old data once the user loads + // the piece detail a second time + PieceActions.updatePiece({}); + PieceStore.unlisten(this.onChange); + UserStore.unlisten(this.onChange); + PieceListStore.unlisten(this.onChange); + }, + + onChange(state) { + this.setState(state); + }, + + loadPiece() { + PieceActions.fetchOne(this.props.params.pieceId); + }, + + handleSubmitSuccess(response) { + PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, + this.state.orderBy, this.state.orderAsc, this.state.filterBy); + + this.loadPiece(); + let notification = new GlobalNotificationModel(response.notification, 'success', 10000); + GlobalNotificationActions.appendGlobalNotification(notification); + }, + + getActions(){ + if (this.state.piece && + this.state.piece.request_action && + this.state.piece.request_action.length > 0) { + return ( + + ); + } + else { + + //We need to disable the normal acl_loan because we're inserting a custom acl_loan button + let availableAcls; + + if(this.state.piece && this.state.piece.acl && typeof this.state.piece.acl.acl_loan !== 'undefined') { + // make a copy to not have side effects + availableAcls = mergeOptions({}, this.state.piece.acl); + availableAcls.acl_loan = false; + } + + return ( + + + + + + + ); + } + }, + + render() { + if('title' in this.state.piece) { + return ( + +
+

{this.state.piece.title}

+ + +
+ + } + subheader={ +
+ + +
+
+ } + buttons={this.getActions()}> + 0}> + + +
+ ); + } else { + return ( +
+ +
+ ); + } + } +}); + +export default IkonotvPieceContainer; diff --git a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_register_piece.js b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_register_piece.js deleted file mode 100644 index 33cc576c..00000000 --- a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_register_piece.js +++ /dev/null @@ -1,80 +0,0 @@ -'use strict'; - -import React from 'react'; -import Router from 'react-router'; - -import WhitelabelActions from '../../../../../actions/whitelabel_actions'; -import WhitelabelStore from '../../../../../stores/whitelabel_store'; - -import PieceListStore from '../../../../../stores/piece_list_store'; -import PieceListActions from '../../../../../actions/piece_list_actions'; - -import UserStore from '../../../../../stores/user_store'; -import UserActions from '../../../../../actions/user_actions'; - -import PieceStore from '../../../../../stores/piece_store'; -import PieceActions from '../../../../../actions/piece_actions'; - -import ContractForm from './ascribe_forms/ikonotv_contract_form'; -import RegisterPieceForm from '../../../../../components/ascribe_forms/form_register_piece'; -import Property from '../../../../../components/ascribe_forms/property'; -import InputCheckbox from '../../../../../components/ascribe_forms/input_checkbox'; - -import GlobalNotificationModel from '../../../../../models/global_notification_model'; -import GlobalNotificationActions from '../../../../../actions/global_notification_actions'; - -import { getLangText } from '../../../../../utils/lang_utils'; -import { mergeOptions } from '../../../../../utils/general_utils'; - - -let IkonotvRegisterPiece = React.createClass({ - - mixins: [Router.Navigation], - - getInitialState(){ - return mergeOptions( - UserStore.getState(), - WhitelabelStore.getState()); - }, - - componentDidMount() { - UserStore.listen(this.onChange); - WhitelabelStore.listen(this.onChange); - UserActions.fetchCurrentUser(); - WhitelabelActions.fetchWhitelabel(); - }, - - componentWillUnmount() { - UserStore.unlisten(this.onChange); - WhitelabelStore.unlisten(this.onChange); - }, - - onChange(state) { - this.setState(state); - }, - - - render() { - if (this.state.currentUser && - this.state.whitelabel && - this.state.whitelabel.user && - this.state.currentUser.email === this.state.whitelabel.user){ - return ( - - ); - } - return ( -
- -
- ); - - } -}); - - -export default IkonotvRegisterPiece; diff --git a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_request_loan.js b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_request_loan.js new file mode 100644 index 00000000..e9c61f51 --- /dev/null +++ b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_request_loan.js @@ -0,0 +1,16 @@ +'use strict'; + +import React from 'react'; + +import ContractForm from '../../../../../components/ascribe_forms/contract_form'; + + +let IkonotvRequestLoan = React.createClass({ + render() { + return ( + + ); + } +}); + +export default IkonotvRequestLoan; \ No newline at end of file diff --git a/js/components/whitelabel/wallet/wallet_app.js b/js/components/whitelabel/wallet/wallet_app.js index f31ec429..06bac15f 100644 --- a/js/components/whitelabel/wallet/wallet_app.js +++ b/js/components/whitelabel/wallet/wallet_app.js @@ -7,21 +7,26 @@ import Footer from '../../footer'; import GlobalNotification from '../../global_notification'; +import getRoutes from './wallet_routes'; + let RouteHandler = Router.RouteHandler; let WalletApp = React.createClass({ mixins: [Router.State], render() { - let header = null; let subdomain = window.location.host.split('.')[0]; + let ROUTES = getRoutes(null, subdomain); + + let header = null; if ((this.isActive('landing') || this.isActive('login') || this.isActive('signup')) && (['ikonotv', 'cyland']).indexOf(subdomain) > -1) { header = (
); } else { - header =
; + header =
; } + return (
{header} diff --git a/js/components/whitelabel/wallet/wallet_routes.js b/js/components/whitelabel/wallet/wallet_routes.js index 435b1695..40f092d6 100644 --- a/js/components/whitelabel/wallet/wallet_routes.js +++ b/js/components/whitelabel/wallet/wallet_routes.js @@ -12,15 +12,16 @@ import PieceList from '../../../components/piece_list'; import PieceContainer from '../../../components/ascribe_detail/piece_container'; import EditionContainer from '../../../components/ascribe_detail/edition_container'; import SettingsContainer from '../../../components/settings_container'; +import RegisterPiece from '../../../components/register_piece'; -// specific components import CylandLanding from './components/cyland/cyland_landing'; import CylandPieceContainer from './components/cyland/ascribe_detail/cyland_piece_container'; import CylandRegisterPiece from './components/cyland/cyland_register_piece'; import CylandPieceList from './components/cyland/cyland_piece_list'; import IkonotvPieceList from './components/ikonotv/ikonotv_piece_list'; -import IkonotvRegisterPiece from './components/ikonotv/ikonotv_register_piece'; +import IkonotvRequestLoan from './components/ikonotv/ikonotv_request_loan'; +import IkonotvPieceContainer from './components/ikonotv/ascribe_detail/ikonotv_piece_container'; import CCRegisterPiece from './components/cc/cc_register_piece'; @@ -40,8 +41,8 @@ let ROUTES = { - - + + @@ -55,8 +56,8 @@ let ROUTES = { - - + + @@ -64,14 +65,16 @@ let ROUTES = { ), 'ikonotv': ( - + + - - - + + + + diff --git a/js/routes.js b/js/routes.js index 65dfbaac..f76e4b45 100644 --- a/js/routes.js +++ b/js/routes.js @@ -5,7 +5,8 @@ import Router from 'react-router'; import getPrizeRoutes from './components/whitelabel/prize/prize_routes'; import getWalletRoutes from './components/whitelabel/wallet/wallet_routes'; -import getDefaultRoutes from './components/routes'; + +import App from './components/ascribe_app'; import PieceList from './components/piece_list'; import PieceContainer from './components/ascribe_detail/piece_container'; @@ -23,19 +24,25 @@ import RegisterPiece from './components/register_piece'; import PrizesDashboard from './components/ascribe_prizes_dashboard/prizes_dashboard'; +import AppConstants from './constants/application_constants'; + let Route = Router.Route; +let Redirect = Router.Redirect; +let baseUrl = AppConstants.baseUrl; const COMMON_ROUTES = ( - + + + - + + - @@ -51,7 +58,7 @@ function getRoutes(type, subdomain) { } else if(type === 'wallet') { routes = getWalletRoutes(COMMON_ROUTES, subdomain); } else { - routes = getDefaultRoutes(COMMON_ROUTES); + routes = COMMON_ROUTES; } return routes; diff --git a/js/utils/general_utils.js b/js/utils/general_utils.js index 70c94a97..15b0e85f 100644 --- a/js/utils/general_utils.js +++ b/js/utils/general_utils.js @@ -26,6 +26,23 @@ export function sanitize(obj, filterFn) { return obj; } +/** + * Removes all falsy values (undefined, null, false, ...) from a list/array + * @param {array} l the array to sanitize + * @return {array} the sanitized array + */ +export function sanitizeList(l) { + let sanitizedList = []; + + for(let i = 0; i < l.length; i++) { + if(l[i]) { + sanitizedList.push(l[i]); + } + } + + return sanitizedList; +} + /** * Sums up a list of numbers. Like a Epsilon-math-kinda-sum... */ diff --git a/sass/main.scss b/sass/main.scss index 34bf42b1..4f197a44 100644 --- a/sass/main.scss +++ b/sass/main.scss @@ -101,10 +101,9 @@ hr { } .img-brand { - padding: 0; - height: 45px; - margin: 5px 0 5px 0; + height: 60px; } + .truncate { white-space: nowrap; width: 4em;