From 43b0239ab8719cff8583f41c609f5cddd29812d1 Mon Sep 17 00:00:00 2001 From: Giulio Cesare Solaroli Date: Wed, 21 Jan 2015 18:29:08 +0100 Subject: [PATCH] Added a few feature based on actual subscription options --- .../Clipperz/PM/DataModel/User.AccountInfo.js | 41 +++++++ .../Clipperz/PM/UI/Components/CardToolbar.js | 4 +- .../Clipperz/PM/UI/Components/Cards/List.js | 4 +- .../Clipperz/PM/UI/Components/ExpiredPanel.js | 2 +- .../PM/UI/Components/Pages/MainPage.js | 7 +- .../Components/Panels/ExtraFeaturesPanel.js | 113 +++++++++++++++--- .../PM/UI/Components/Panels/MainPanel.js | 19 +-- .../delta/js/Clipperz/PM/UI/MainController.js | 19 ++- 8 files changed, 173 insertions(+), 36 deletions(-) diff --git a/frontend/delta/js/Clipperz/PM/DataModel/User.AccountInfo.js b/frontend/delta/js/Clipperz/PM/DataModel/User.AccountInfo.js index 71904ed..b57795b 100644 --- a/frontend/delta/js/Clipperz/PM/DataModel/User.AccountInfo.js +++ b/frontend/delta/js/Clipperz/PM/DataModel/User.AccountInfo.js @@ -33,6 +33,41 @@ Clipperz.PM.DataModel.User.AccountInfo = function(args) { Clipperz.Base.extend(Clipperz.PM.DataModel.User.AccountInfo, Object, { + 'status': function () { + return this._attributes['currentSubscriptionType']; + }, + + 'level': function () { + var result; + + // -1.00 -> EARLY_ADOPTER + // 2.00 -> FRIEND + // 5.00 -> FAN + // 10.00 -> DEVOTEE + // 15.00 -> PATRON + + if (this.status() == 'FRIEND') { + result = 1; + } else if (this.status() == 'FAN') { + result = 2; + } else if (this.status() == 'DEVOTEE') { + result = 3; + } else if (this.status() == 'PATRON') { + result = 4; + } else { + result = 0; + }; + + return result; +// return this._attributes['latestActiveThreshold']; + }, + + 'isExpiring': function () { + return this._attributes['isExpiring']; + }, + + //............................................................................ + 'features': function () { return this._attributes['features']; }, @@ -47,6 +82,12 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User.AccountInfo, Object, { 'to': this._attributes['expirationDate'] }; }, + + 'isExpired': function () { + return this._attributes['isExpired']; + }, + + //========================================================================= __syntaxFix__: "syntax fix" diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/CardToolbar.js b/frontend/delta/js/Clipperz/PM/UI/Components/CardToolbar.js index 5503666..36e0c61 100644 --- a/frontend/delta/js/Clipperz/PM/UI/Components/CardToolbar.js +++ b/frontend/delta/js/Clipperz/PM/UI/Components/CardToolbar.js @@ -29,7 +29,7 @@ Clipperz.PM.UI.Components.CardToolbar = React.createClass({ // 'style': React.PropTypes.oneOf(['extra-short', 'narrow', 'wide', 'extra-wide']).isRequired, 'style': React.PropTypes.oneOf(Clipperz_PM_UI_availableStyles).isRequired, 'enableSidePanels': React.PropTypes.bool.isRequired, - 'accountStatus': React.PropTypes.object.isRequired, + 'accountInfo': React.PropTypes.object.isRequired, 'messageBox': React.PropTypes.object.isRequired, 'filter': React.PropTypes.object /*.isRequired */ }, @@ -99,7 +99,7 @@ Clipperz.PM.UI.Components.CardToolbar = React.createClass({ return React.DOM.div({className:'cardToolbar ' + this.props['style']}, [ // React.DOM.div({className:'header'}, this.props['enableSidePanels'] ? this.renderWithSidePanels() : this.renderWithoutSidePanels()), React.DOM.header({}, this.props['enableSidePanels'] ? this.renderWithSidePanels() : this.renderWithoutSidePanels()), - Clipperz.PM.UI.Components.AccountStatus(this.props['accountStatus']), + Clipperz.PM.UI.Components.AccountStatus(this.props['accountInfo']), Clipperz.PM.UI.Components.MessageBox(this.props['messageBox']), ]); } diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/Cards/List.js b/frontend/delta/js/Clipperz/PM/UI/Components/Cards/List.js index f5930f7..dc61a6f 100644 --- a/frontend/delta/js/Clipperz/PM/UI/Components/Cards/List.js +++ b/frontend/delta/js/Clipperz/PM/UI/Components/Cards/List.js @@ -30,6 +30,7 @@ Clipperz.PM.UI.Components.Cards.List = React.createClass({ propTypes: { 'cards': React.PropTypes.array, + 'featureSet': React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL', 'OFFLINE']).isRequired, 'selectedCard': React.PropTypes.object }, @@ -62,7 +63,8 @@ Clipperz.PM.UI.Components.Cards.List = React.createClass({ var cards = this.props['cards'] ? this.props['cards'] : []; var classes = { 'cardList': true, - 'loadingCard': this.props['selectedCard'] && this.props['selectedCard']['_reference'] && this.props['selectedCard']['loading'] + 'loadingCard': this.props['selectedCard'] && this.props['selectedCard']['_reference'] && this.props['selectedCard']['loading'], + 'EXPIRED': this.props['featureSet'] == 'EXPIRED', }; classes[this.props['style']] = true; diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/ExpiredPanel.js b/frontend/delta/js/Clipperz/PM/UI/Components/ExpiredPanel.js index 090c279..45bb37e 100644 --- a/frontend/delta/js/Clipperz/PM/UI/Components/ExpiredPanel.js +++ b/frontend/delta/js/Clipperz/PM/UI/Components/ExpiredPanel.js @@ -26,7 +26,7 @@ Clipperz.Base.module('Clipperz.PM.UI.Components'); Clipperz.PM.UI.Components.ExpiredPanel = React.createClass({ propTypes: { -// featureSet: React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL']).isRequired, +// featureSet: React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL', 'OFFLINE']).isRequired, // 'level': React.PropTypes.oneOf(['hide', 'info', 'warning', 'error']).isRequired }, diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/Pages/MainPage.js b/frontend/delta/js/Clipperz/PM/UI/Components/Pages/MainPage.js index 4eef72b..866c31b 100644 --- a/frontend/delta/js/Clipperz/PM/UI/Components/Pages/MainPage.js +++ b/frontend/delta/js/Clipperz/PM/UI/Components/Pages/MainPage.js @@ -28,13 +28,14 @@ Clipperz.PM.UI.Components.Pages.MainPage = React.createClass({ getDefaultProps: function () { return { - } + featureSet: 'FULL' + }; }, propTypes: { 'messageBox': React.PropTypes.object.isRequired, -// 'featureSet': React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL']), - 'accountStatus': React.PropTypes.object.isRequired, + 'featureSet': React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL', 'OFFLINE']).isRequired, + 'accountInfo': React.PropTypes.object.isRequired, // 'mediaQueryStyle': React.PropTypes.oneOf(['extra-short', 'narrow', 'wide', 'extra-wide']).isRequired, 'style': React.PropTypes.oneOf(Clipperz_PM_UI_availableStyles).isRequired, // 'cards': React.PropTypes.deferred.isRequired diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/Panels/ExtraFeaturesPanel.js b/frontend/delta/js/Clipperz/PM/UI/Components/Panels/ExtraFeaturesPanel.js index f8bcd4d..c53aee4 100644 --- a/frontend/delta/js/Clipperz/PM/UI/Components/Panels/ExtraFeaturesPanel.js +++ b/frontend/delta/js/Clipperz/PM/UI/Components/Panels/ExtraFeaturesPanel.js @@ -34,10 +34,32 @@ Clipperz.PM.UI.Components.Panels.ExtraFeaturesPanel = React.createClass({ handleDownloadOfflineCopyLink: function (anEvent) { MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'downloadOfflineCopy'); }, - + + propTypes: { + 'accountInfo': React.PropTypes.object.isRequired, + }, + + getInitialState: function() { + return { + 'account': false, + 'subscription': false, + 'data': false, + }; + }, + + toggleState: function (section) { + return MochiKit.Base.bind(function () { + var newState = {}; + + newState[section] = !this.state[section]; + this.setState(newState); + }, this); + }, + //========================================================================= render: function () { +//console.log("ExtraFeaturesPanel props", this.props); var classes = { 'panel': true, 'right': true, @@ -53,32 +75,93 @@ Clipperz.PM.UI.Components.Panels.ExtraFeaturesPanel = React.createClass({ React.DOM.div({}, [ React.DOM.ul({}, [ - React.DOM.li({}, [ - React.DOM.h1({}, "Account"), + React.DOM.li({'className':this.state['account'] ? 'open' : 'closed'}, [ + React.DOM.h1({'onClick':this.toggleState('account')}, "Account"), + React.DOM.ul({}, [ + React.DOM.li({}, [ + React.DOM.h2({}, "Passphrase"), + React.DOM.div({}, [ + React.DOM.p({}, "") + ]) + ]), + React.DOM.li({}, [ + React.DOM.h2({}, "One Time Passwords"), + React.DOM.div({}, [ + React.DOM.p({}, "") + ]) + ]), + React.DOM.li({}, [ + React.DOM.h2({}, "Preferences"), + React.DOM.div({}, [ + React.DOM.p({}, "") + ]) + ]), + React.DOM.li({}, [ + React.DOM.h2({}, "Delete account"), + React.DOM.div({}, [ + React.DOM.p({}, "") + ]) + ]) + ]) ]), - React.DOM.li({}, [ - React.DOM.h1({}, "Data"), + React.DOM.li({'className':this.state['subscription'] ? 'open' : 'closed'}, [ + React.DOM.h1({'onClick':this.toggleState('subscription')}, "Subscription"), + React.DOM.ul({}, [ + React.DOM.li({}, [ + React.DOM.h2({}, "x1"), + React.DOM.div({}, [ + React.DOM.p({}, "") + ]) + ]), + React.DOM.li({}, [ + React.DOM.h2({}, "x2"), + React.DOM.div({}, [ + React.DOM.p({}, "") + ]) + ]), + React.DOM.li({}, [ + React.DOM.h2({}, "x3"), + React.DOM.div({}, [ + React.DOM.p({}, "") + ]) + ]), + React.DOM.li({}, [ + React.DOM.h2({}, "x4"), + React.DOM.div({}, [ + React.DOM.p({}, "") + ]) + ]) + ]) + ]), + React.DOM.li({'className':this.state['data'] ? 'open' : 'closed'}, [ + React.DOM.h1({'onClick':this.toggleState('data')}, "Data"), React.DOM.ul({}, [ React.DOM.li({}, [ React.DOM.h2({}, "Offline copy"), React.DOM.div({}, [ React.DOM.p({}, "With just one click you can dump all your encrypted data from Clipperz servers to your hard disk and create a read-only offline version of Clipperz to be used when you are not connected to the Internet."), - React.DOM.a({'onClick':this.handleDownloadOfflineCopyLink}, "Download") + React.DOM.a({'className':'button', 'onClick':this.handleDownloadOfflineCopyLink}, "Download") + ]) + ]), + React.DOM.li({}, [ + React.DOM.h2({}, "Import"), + React.DOM.div({}, [ + React.DOM.p({}, "") + ]) + ]), + React.DOM.li({}, [ + React.DOM.h2({}, "Export"), + React.DOM.div({}, [ + React.DOM.p({}, "") ]) ]), React.DOM.li({}, [ React.DOM.h2({}, "Sharing"), - ]), - React.DOM.li({}, [ - React.DOM.h2({}, "Import"), - ]), - React.DOM.li({}, [ - React.DOM.h2({}, "Export"), + React.DOM.div({}, [ + React.DOM.p({}, "") + ]) ]) ]) - ]), - React.DOM.li({}, [ - React.DOM.h1({}, "Tools"), ]) ]) ]) diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/Panels/MainPanel.js b/frontend/delta/js/Clipperz/PM/UI/Components/Panels/MainPanel.js index 868b4fc..894fce6 100644 --- a/frontend/delta/js/Clipperz/PM/UI/Components/Panels/MainPanel.js +++ b/frontend/delta/js/Clipperz/PM/UI/Components/Panels/MainPanel.js @@ -30,16 +30,10 @@ Clipperz.PM.UI.Components.Panels.MainPanel = React.createClass({ propTypes: { 'messageBox': React.PropTypes.object.isRequired, - 'featureSet': React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL']).isRequired, + 'featureSet': React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL', 'OFFLINE']).isRequired, 'style': React.PropTypes.oneOf(Clipperz_PM_UI_availableStyles).isRequired, }, - getDefaultProps: function () { - return { - featureSet: 'FULL' - }; - }, - style: function () { return this.props['style']; }, @@ -66,7 +60,13 @@ Clipperz.PM.UI.Components.Panels.MainPanel = React.createClass({ }, renderCardFrame: function (firstColumnComponents, secondColumnComponents) { - var addCardButton = React.DOM.div({'className': 'addCardButton', 'onClick':this.handleAddCardClick}, 'add card'); + var addCardButton; + + if ((this.props['featureSet'] != 'EXPIRED') && (this.props['featureSet'] != 'OFFLINE')) { + addCardButton = React.DOM.div({'className': 'addCardButton', 'onClick':this.handleAddCardClick}, 'add card'); + } else { + addCardButton = null; + } return React.DOM.div({'key':'cardContent', 'className':'cardContent'}, [ React.DOM.div({'className':'cardListColumn column'}, [addCardButton, firstColumnComponents]), @@ -82,7 +82,8 @@ Clipperz.PM.UI.Components.Panels.MainPanel = React.createClass({ cardToolbarProps = MochiKit.Base.merge(this.props, { 'key': 'toolbar', 'style': this.style(), - 'enableSidePanels': (this.props['featureSet'] != 'EXPIRED') +// 'enableSidePanels': (this.props['featureSet'] != 'EXPIRED') + 'enableSidePanels': true, }); return Clipperz.PM.UI.Components.CardToolbar(cardToolbarProps); diff --git a/frontend/delta/js/Clipperz/PM/UI/MainController.js b/frontend/delta/js/Clipperz/PM/UI/MainController.js index ade0531..201f381 100644 --- a/frontend/delta/js/Clipperz/PM/UI/MainController.js +++ b/frontend/delta/js/Clipperz/PM/UI/MainController.js @@ -581,6 +581,7 @@ console.log("THE BROWSER IS OFFLINE"); var fullFilterCriteria; var selectedCardReference = this._selectedCardInfo ? this._selectedCardInfo['reference'] : null; +//console.log("updateSelectedCards - ACCOUNT INFO.featureSet", this.userAccountInfo()['featureSet']); if (aFilter['type'] == 'ALL') { filterCriteria = MochiKit.Base.operator.truth; sortCriteria = Clipperz.Base.caseInsensitiveKeyComparator('label'); @@ -884,7 +885,7 @@ console.log("THE BROWSER IS OFFLINE"); level = 'HIDE'; //console.log("messageBox - this.user()", this.user()); - if (this.user() != null && this.user().accountInfo() != null && this.user().accountInfo().featureSet() == 'EXPIRED') { + if (this.userAccountInfo()['featureSet'] == 'EXPIRED') { message = "Exprired subscription"; level = 'ERROR'; } @@ -905,6 +906,7 @@ console.log("THE BROWSER IS OFFLINE"); 'currentSubscriptionType', 'expirationDate', 'featureSet', + 'features', 'isExpired', 'isExpiring', 'paymentVerificationPending' @@ -944,9 +946,10 @@ console.log("THE BROWSER IS OFFLINE"); } else if (aPageName == 'mainPage') { extraProperties = { 'messageBox': this.messageBoxContent(), - 'accountStatus': this.userAccountInfo(), + 'accountInfo': this.userAccountInfo(), 'selectionPanelStatus': this.isSelectionPanelOpen() ? 'OPEN' : 'CLOSED', 'settingsPanelStatus': this.isSettingsPanelOpen() ? 'OPEN' : 'CLOSED', + 'featureSet': this.userAccountInfo()['featureSet'], // 'shouldIncludeArchivedCards': this.shouldIncludeArchivedCards(), // 'cards': …, // 'tags': …, @@ -1135,9 +1138,15 @@ console.log("THE BROWSER IS OFFLINE"); }, selectCard_handler: function (someInfo, shouldUpdateCardDetail) { - this._selectedCardInfo = someInfo; - this.refreshSelectedCards(); - this.updateSelectedCard(someInfo, true, shouldUpdateCardDetail); + if (this.userAccountInfo()['featureSet'] != 'EXPIRED') { + this._selectedCardInfo = someInfo; + this.refreshSelectedCards(); + this.updateSelectedCard(someInfo, true, shouldUpdateCardDetail); + +// # TODO: make the selected element visible; +// this may not always be the case, as selection can also be changed using keys. +// MochiKit.Visual.ScrollTo(MochiKit.DOM.getElement("xxx")); + } }, refreshCardEditDetail_handler: function (aRecordReference) {