1
0
mirror of http://git.whoc.org.uk/git/password-manager.git synced 2025-10-28 18:07:35 +01:00

Many small overall UI improvements

- proxy are now aware of their respective features;
- updated account status info and added also proxy info (especially to show when using an offline copy)
- conditionally enabled different features across the UI, based on user account / proxy available features
This commit is contained in:
Giulio Cesare Solaroli
2015-03-10 22:59:24 +01:00
parent 3d809a71db
commit 187959fd1e
19 changed files with 270 additions and 132 deletions

View File

@@ -131,6 +131,20 @@ Clipperz.PM.Proxy.prototype = MochiKit.Base.update(null, {
//=========================================================================
'type': function () {
throw Clipperz.Base.exception.AbstractMethod;
},
'typeDescription': function () {
throw Clipperz.Base.exception.AbstractMethod;
},
'features': function (someFeatures) {
throw Clipperz.Base.exception.AbstractMethod;
},
//=========================================================================
'processMessage': function (aFunctionName, someParameters, aRequestType) {
var deferredResult;

View File

@@ -46,6 +46,20 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.JSON, Clipperz.PM.Proxy, {
return this._url;
},
//=========================================================================
'type': function () {
return 'ONLINE';
},
'typeDescription': function () {
return 'Online service';
},
'features': function (someFeatures) {
return someFeatures;
},
//=========================================================================
'_sendMessage': function(aFunctionName, aVersion, someParameters) {

View File

@@ -21,6 +21,7 @@ refer to http://www.clipperz.com.
*/
if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
@@ -32,6 +33,8 @@ Clipperz.PM.Proxy.Offline = function(args) {
Clipperz.PM.Proxy.Offline.superclass.constructor.call(this, args);
this._dataStore = args.dataStore || new Clipperz.PM.Proxy.Offline.DataStore(args);
this._type = args.type || 'OFFLINE';
this._typeDescription = args.typeDescription || 'Offline';
return this;
}
@@ -64,7 +67,29 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.Offline, Clipperz.PM.Proxy, {
return this.dataStore().canRegisterNewUsers();
},
//-------------------------------------------------------------------------
//=========================================================================
'type': function () {
return this._type;
},
'typeDescription': function () {
return this._typeDescription;
},
'features': function (someFeatures) {
var result;
if (this.type() == 'OFFLINE_COPY') {
result = ['LIST_CARDS', 'CARD_DETAILS'];
} else {
result = someFeatures;
}
return result;
},
//=========================================================================
__syntaxFix__: "syntax fix"

View File

@@ -21,6 +21,7 @@ refer to http://www.clipperz.com.
*/
"use strict";
Clipperz.Base.module('Clipperz.PM.UI.Components');
Clipperz.PM.UI.Components.AccountStatus = React.createClass({
@@ -28,7 +29,9 @@ Clipperz.PM.UI.Components.AccountStatus = React.createClass({
propTypes: {
// 'currentSubscriptionType': React.PropTypes.oneOf(['EARLY_ADOPTER', 'FRIEND', 'FAN', 'DEVOTEE', 'PATRON', 'TRIAL', 'TRIAL_EXPIRED', 'PAYMENT_FAILED_2', 'EXPIRED', 'PAYMENT_FAILED', 'VERIFYING_PAYMENT', 'VERIFYING_PAYMENT_2']).isRequired,
'expirationDate': React.PropTypes.string /* .isRequired */,
'referenceDate': React.PropTypes.string /* .isRequired */,
'featureSet': React.PropTypes.oneOf(['TRIAL', 'EXPIRED', 'FULL']) /* .isRequired */ ,
'proxyType': React.PropTypes.oneOf(['ONLINE', 'OFFLINE', 'OFFLINE_COPY']),
'isExpired': React.PropTypes.bool /* .isRequired */ ,
'isExpiring': React.PropTypes.bool /* .isRequired */ ,
'paymentVerificationPending': React.PropTypes.bool /* .isRequired */ ,
@@ -38,18 +41,30 @@ Clipperz.PM.UI.Components.AccountStatus = React.createClass({
render: function () {
//console.log("AccountStatus props", this.props);
var classes = {
var accountInfoClasses = {
'accountStatus': true,
'isExpiring': this.props['isExpiring'],
'isExpired': this.props['isExpired'],
};
accountInfoClasses[this.props['featureSet']] = true;
classes[this.props['featureSet']] = true;
var proxyInfoClasses = {
'proxyInfo': true
}
proxyInfoClasses[this.props['proxyType']] = true;
return React.DOM.div({'className':React.addons.classSet(classes)}, [
React.DOM.span({'className': 'level'}, this.props['featureSet']),
React.DOM.span({'className': 'expirationDate'}, this.props['expirationDate'])
return React.DOM.div({'className':'miscInfo'}, [
React.DOM.div({'className':React.addons.classSet(proxyInfoClasses)}, [
React.DOM.span({'className':'proxyDescription'}, this.props['proxyTypeDescription']),
React.DOM.span({'className':'referenceDate'}, this.props['referenceDate'])
]),
React.DOM.div({'className':React.addons.classSet(accountInfoClasses)}, [
React.DOM.span({'className':'level'}, Clipperz.PM.DataModel.Feature['featureSets'][this.props['featureSet']]),
React.DOM.span({'className':'expirationMessage'}, "expiring on"),
React.DOM.span({'className':'expirationDate'}, this.props['expirationDate'])
])
]);
}
//=========================================================================

View File

@@ -30,6 +30,7 @@ Clipperz.PM.UI.Components.CardToolbar = React.createClass({
'style': React.PropTypes.oneOf(Clipperz_PM_UI_availableStyles).isRequired,
'enableSidePanels': React.PropTypes.bool.isRequired,
'accountInfo': React.PropTypes.object.isRequired,
'proxyInfo': React.PropTypes.object.isRequired,
'messageBox': React.PropTypes.object.isRequired,
'filter': React.PropTypes.object /*.isRequired */
},
@@ -99,7 +100,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['accountInfo']),
Clipperz.PM.UI.Components.AccountStatus(MochiKit.Base.update(this.props['accountInfo'], this.props['proxyInfo'])),
Clipperz.PM.UI.Components.MessageBox(this.props['messageBox']),
]);
}

View File

@@ -31,6 +31,15 @@ Clipperz.PM.UI.Components.Cards.CommandToolbar = React.createClass({
propTypes: {
// 'label': React.PropTypes.string.isRequired,
// 'loading': React.PropTypes.bool,
'features': React.PropTypes.array.isRequired,
},
features: function () {
return this.props['features'];
},
isFeatureEnabled: function (aValue) {
return (this.features().indexOf(aValue) > -1);
},
//----------------------------------------------------------------------------
@@ -40,29 +49,17 @@ Clipperz.PM.UI.Components.Cards.CommandToolbar = React.createClass({
},
//----------------------------------------------------------------------------
// EDIT_CARD -> archive, edit
// DELETE_CARD -> delete
// ADD_CARD -> clone
commands: function () {
var archiveLabel = this.props['_isArchived'] ? "restore" : "archive";
return {
'delete': {
'label': "delete",
'broadcastEvent': 'deleteCard'
},
'archive': {
'label': this.props['_isArchived'] ? "restore" : "archive",
'broadcastEvent': 'toggleArchiveCard'
},
// 'share': {
// 'label': "share",
// 'broadcastEvent': 'shareCard'
// },
'clone': {
'label': "clone",
'broadcastEvent': 'cloneCard'
},
'edit': {
'label': "edit",
'broadcastEvent': 'editCard'
}
'delete': { 'label': "delete", 'broadcastEvent': 'deleteCard', 'enabled': this.isFeatureEnabled('DELETE_CARD')},
'archive': { 'label': archiveLabel, 'broadcastEvent': 'toggleArchiveCard', 'enabled': this.isFeatureEnabled('EDIT_CARD')},
// 'share': { 'label': "share", 'broadcastEvent': 'shareCard' },
'clone': { 'label': "clone", 'broadcastEvent': 'cloneCard', 'enabled': this.isFeatureEnabled('ADD_CARD')},
'edit': { 'label': "edit", 'broadcastEvent': 'editCard', 'enabled': this.isFeatureEnabled('EDIT_CARD')}
};
},
@@ -92,7 +89,12 @@ Clipperz.PM.UI.Components.Cards.CommandToolbar = React.createClass({
}
return React.DOM.ul({}, MochiKit.Base.map(function (aCommand) {
return React.DOM.li({'className':aCommand['broadcastEvent'], 'onClick':commandHandler, 'data-broadcast-event':aCommand['broadcastEvent']}, [React.DOM.span({}, aCommand['label'])]);
var classes = {};
classes[aCommand['broadcastEvent']] = true;
classes['enabled'] = aCommand['enabled'];
classes['disabled'] = !aCommand['enabled'];
return React.DOM.li({'className':React.addons.classSet(classes), 'onClick':aCommand['enabled'] ? commandHandler : null, 'data-broadcast-event':aCommand['broadcastEvent']}, [React.DOM.span({}, aCommand['label'])]);
}, commandValues));
},

View File

@@ -34,6 +34,7 @@ Clipperz.PM.UI.Components.Cards.Detail = React.createClass({
result = this.props['selectedCard'];
if (result) {
result['features'] = this.props['features'];
result['style'] = this.props['style'];
result['ask'] = (this.props['style'] == 'narrow') ? this.props['ask'] : null;
result['showGlobalMask'] = this.props['showGlobalMask'];

View File

@@ -30,12 +30,23 @@ Clipperz.PM.UI.Components.Cards.List = React.createClass({
propTypes: {
'cards': React.PropTypes.array,
'featureSet': React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL', 'OFFLINE']).isRequired,
'featureSet': React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL']).isRequired,
'features': React.PropTypes.array.isRequired,
'selectedCard': React.PropTypes.object
},
features: function () {
return this.props['features'];
},
isFeatureEnabled: function (aValue) {
return (this.features().indexOf(aValue) > -1);
},
handleClick: function (anEvent) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'selectCard', {'reference':anEvent.currentTarget.dataset.reference, 'label':anEvent.currentTarget.dataset.label}, true);
if (this.isFeatureEnabled('CARD_DETAILS')) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'selectCard', {'reference':anEvent.currentTarget.dataset.reference, 'label':anEvent.currentTarget.dataset.label}, true);
}
},
renderItem: function (anItem) {
@@ -70,7 +81,7 @@ Clipperz.PM.UI.Components.Cards.List = React.createClass({
classes[this.props['style']] = true;
return React.DOM.div({'key':'cardList', 'className':React.addons.classSet(classes)}, [
React.DOM.ul({}, MochiKit.Base.map(this.renderItem, cards))
this.isFeatureEnabled('LIST_CARDS') ? React.DOM.ul({}, MochiKit.Base.map(this.renderItem, cards)) : null
]);
},

View File

@@ -62,7 +62,6 @@ Clipperz.PM.UI.Components.Cards.View = React.createClass({
newState[aField['_reference']] = !this.state[aField['_reference']];
return function () {
console.log("SHOW PASSWORD", aField, self);
self.setState(newState);
};
},

View File

@@ -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', 'OFFLINE']).isRequired,
// featureSet: React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL']).isRequired,
// 'level': React.PropTypes.oneOf(['hide', 'info', 'warning', 'error']).isRequired
},

View File

@@ -28,7 +28,8 @@ Clipperz.PM.UI.Components.Pages.MainPage = React.createClass({
getDefaultProps: function () {
return {
featureSet: 'FULL'
featureSet: 'FULL',
features: []
};
},
@@ -36,7 +37,8 @@ Clipperz.PM.UI.Components.Pages.MainPage = React.createClass({
'tags': React.PropTypes.object,
'allTags': React.PropTypes.array,
'messageBox': React.PropTypes.object.isRequired,
'featureSet': React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL', 'OFFLINE']).isRequired,
'featureSet': React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL']).isRequired,
'features': React.PropTypes.array.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,

View File

@@ -36,7 +36,7 @@ Clipperz.PM.UI.Components.Panels.ExtraFeaturesPanel = React.createClass({
},
propTypes: {
'accountInfo': React.PropTypes.object.isRequired,
'accountInfo': React.PropTypes.object.isRequired,
},
getInitialState: function() {
@@ -90,6 +90,12 @@ Clipperz.PM.UI.Components.Panels.ExtraFeaturesPanel = React.createClass({
React.DOM.p({}, "")
])
]),
React.DOM.li({}, [
React.DOM.h2({}, "Device PIN"),
React.DOM.div({}, [
React.DOM.p({}, "Configure a PIN that will allow to get access to your cards, but only on this device.")
])
]),
React.DOM.li({}, [
React.DOM.h2({}, "Preferences"),
React.DOM.div({}, [

View File

@@ -31,7 +31,8 @@ Clipperz.PM.UI.Components.Panels.MainPanel = React.createClass({
propTypes: {
'allTags': React.PropTypes.array,
'messageBox': React.PropTypes.object.isRequired,
'featureSet': React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL', 'OFFLINE']).isRequired,
'featureSet': React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL']).isRequired,
'features': React.PropTypes.array.isRequired,
'style': React.PropTypes.oneOf(Clipperz_PM_UI_availableStyles).isRequired,
},
@@ -43,6 +44,14 @@ Clipperz.PM.UI.Components.Panels.MainPanel = React.createClass({
return this.props['featureSet'];
},
features: function () {
return this.props['features'];
},
isFeatureEnabled: function (aValue) {
return (this.features().indexOf(aValue) > -1);
},
handleMaskClick: function () {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'maskClick');
},
@@ -62,15 +71,22 @@ Clipperz.PM.UI.Components.Panels.MainPanel = React.createClass({
renderCardFrame: function (firstColumnComponents, secondColumnComponents) {
var addCardButton;
var cardColumnClasses;
if ((this.props['featureSet'] != 'EXPIRED') && (this.props['featureSet'] != 'OFFLINE')) {
// if ((this.props['featureSet'] != 'EXPIRED') && (this.props['featureSet'] != 'OFFLINE')) {
if (this.isFeatureEnabled('ADD_CARD')) {
addCardButton = React.DOM.div({'className': 'addCardButton', 'onClick':this.handleAddCardClick}, 'add card');
} else {
addCardButton = null;
}
cardColumnClasses = {
'cardListColumn': true,
'column': true,
'addCard': this.isFeatureEnabled('ADD_CARD')
}
return React.DOM.div({'key':'cardContent', 'className':'cardContent'}, [
React.DOM.div({'className':'cardListColumn column'}, [addCardButton, firstColumnComponents]),
React.DOM.div({'className':React.addons.classSet(cardColumnClasses)}, [addCardButton, firstColumnComponents]),
React.DOM.div({'className':'cardDetail column right'}, secondColumnComponents)
])
},

View File

@@ -50,7 +50,7 @@ Clipperz.PM.UI.MainController = function() {
this._selectedCardInfo = null;
this._closeMaskAction = null;
this._pages = {};
this.renderPages([
'loginPage',
@@ -462,6 +462,7 @@ console.log("THE BROWSER IS OFFLINE");
showCardDetailInNarrowView: function (aValue) {
return Clipperz.Async.callbacks("MainController.showCardDetailInNarrowView", [
MochiKit.Base.method(this, 'setPageProperties', 'cardDetailPage', 'features', this.features()),
MochiKit.Base.method(this, 'setPageProperties', 'cardDetailPage', 'selectedCard', aValue),
MochiKit.Base.method(this, 'moveInPage', this.currentPage(), 'cardDetailPage'),
], {trace:false});
@@ -581,7 +582,6 @@ 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');
@@ -896,25 +896,17 @@ console.log("THE BROWSER IS OFFLINE");
//-------------------------------------------------------------------------
messageBoxContent: function () {
var message;
var level;
message = "";
level = 'HIDE';
//console.log("messageBox - this.user()", this.user());
if (this.userAccountInfo()['featureSet'] == 'EXPIRED') {
message = "Exprired subscription";
level = 'ERROR';
}
return {
'message': message,
'level': level
};
featureSet: function () {
return this.userAccountInfo()['featureSet'];
},
features: function () {
// return this.userAccountInfo()['features'];
return Clipperz.PM.Proxy.defaultProxy.features(this.userAccountInfo()['features']);
},
//.........................................................................
userAccountInfo: function () {
var result;
@@ -924,6 +916,7 @@ console.log("THE BROWSER IS OFFLINE");
var usefulFields = [
'currentSubscriptionType',
'expirationDate',
'referenceDate',
'featureSet',
'features',
'isExpired',
@@ -939,12 +932,40 @@ console.log("THE BROWSER IS OFFLINE");
return result;
},
proxyInfo: function () {
return {
'proxyType': Clipperz.PM.Proxy.defaultProxy.type(),
'proxyTypeDescription': Clipperz.PM.Proxy.defaultProxy.typeDescription()
}
},
//-------------------------------------------------------------------------
messageBoxContent: function () {
var message;
var level;
message = "";
level = 'HIDE';
//console.log("messageBox - this.user()", this.user());
if (this.featureSet() == 'EXPIRED') {
message = "Exprired subscription";
level = 'ERROR';
}
return {
'message': message,
'level': level
};
},
genericPageProperties: function () {
return {
'style': this.mediaQueryStyle(),
'isTouchDevice': this.isTouchDevice(),
'isDesktop': this.isDesktop(),
'isTouchDevice': this.isTouchDevice(),
'isDesktop': this.isDesktop(),
'hasKeyboard': this.hasKeyboard()
};
},
@@ -968,7 +989,9 @@ console.log("THE BROWSER IS OFFLINE");
'accountInfo': this.userAccountInfo(),
'selectionPanelStatus': this.isSelectionPanelOpen() ? 'OPEN' : 'CLOSED',
'settingsPanelStatus': this.isSettingsPanelOpen() ? 'OPEN' : 'CLOSED',
'featureSet': this.userAccountInfo()['featureSet'],
'featureSet': this.featureSet(),
'features': this.features(),
'proxyInfo': this.proxyInfo(),
// 'shouldIncludeArchivedCards': this.shouldIncludeArchivedCards(),
// 'cards': …,
// 'tags': …,
@@ -1156,7 +1179,7 @@ console.log("THE BROWSER IS OFFLINE");
selectCard: function (someInfo, shouldUpdateCardDetail) {
var result;
if (this.userAccountInfo()['featureSet'] != 'EXPIRED') {
if (this.featureSet() != 'EXPIRED') {
this._selectedCardInfo = someInfo;
this.refreshSelectedCards();
result = this.updateSelectedCard(someInfo, true, shouldUpdateCardDetail);