diff --git a/app/browser/reducers/ledgerReducer.js b/app/browser/reducers/ledgerReducer.js index 0ec59546bed..fe7525bc701 100644 --- a/app/browser/reducers/ledgerReducer.js +++ b/app/browser/reducers/ledgerReducer.js @@ -522,6 +522,11 @@ const ledgerReducer = (state, action, immutableAction) => { state = aboutPreferencesState.setBackupStatus(state, true) break } + case appConstants.APP_ON_PUBLISHER_TOGGLE_UPDATE: + { + state = ledgerApi.pageDataChanged(state, {}, true) + break + } } return state } diff --git a/app/common/lib/ledgerUtil.js b/app/common/lib/ledgerUtil.js index 129c50b9a8a..cf146890b36 100644 --- a/app/common/lib/ledgerUtil.js +++ b/app/common/lib/ledgerUtil.js @@ -26,6 +26,7 @@ const {responseHasContent} = require('./httpUtil') const urlUtil = require('../../../js/lib/urlutil') const getSetting = require('../../../js/settings').getSetting const urlParse = require('../urlParse') +const {makeImmutable} = require('../state/immutableUtil') /** * Is page an actual page being viewed by the user? (not an error page, etc) @@ -591,6 +592,44 @@ const getMediaProvider = (url, firstPartyUrl, referrer) => { return provider } +const hasRequiredVisits = (state, publisherKey) => { + state = makeImmutable(state) || Immutable.Map() + + const minimumVisits = parseInt(getSetting(settings.PAYMENTS_MINIMUM_VISITS)) + + if (minimumVisits === 1) { + return true + } + + const publisher = ledgerState.getPublisher(state, publisherKey) + const publisherVisits = publisher.get('visits') + + if (typeof publisherVisits !== 'number') { + return minimumVisits === 1 + } + + const visitDifference = minimumVisits - publisherVisits + + return (visitDifference === 1) +} + +const getRemainingRequiredTime = (state, publisherKey) => { + state = makeImmutable(state) || Immutable.Map() + + const minimumVisitTime = parseInt(getSetting(settings.PAYMENTS_MINIMUM_VISIT_TIME)) + const publisher = ledgerState.getPublisher(state, publisherKey) + const publisherDuration = publisher.get('duration') + + if ( + typeof publisherDuration !== 'number' || + publisherDuration >= minimumVisitTime + ) { + return minimumVisitTime + } + + return (minimumVisitTime - publisherDuration) +} + const defaultMonthlyAmounts = Immutable.List([5.0, 7.5, 10.0, 17.5, 25.0, 50.0, 75.0, 100.0]) const milliseconds = { @@ -625,7 +664,9 @@ const getMethods = () => { defaultMonthlyAmounts, getDefaultMediaFavicon, generateMediaCacheData, - shouldShowMenuOption + shouldShowMenuOption, + hasRequiredVisits, + getRemainingRequiredTime } let privateMethods = {} diff --git a/app/renderer/components/navigation/publisherToggle.js b/app/renderer/components/navigation/publisherToggle.js index 1dc70bab9bb..0b3e01b893c 100644 --- a/app/renderer/components/navigation/publisherToggle.js +++ b/app/renderer/components/navigation/publisherToggle.js @@ -33,6 +33,7 @@ const fundUnverifiedPublisherImage = require('../../../extensions/brave/img/urlb class PublisherToggle extends React.Component { constructor (props) { super(props) + this.mounted = false this.onAuthorizePublisher = this.onAuthorizePublisher.bind(this) } @@ -58,6 +59,50 @@ class PublisherToggle extends React.Component { } } + setUpdateTimeout () { + const shouldSetTimeout = ledgerUtil.hasRequiredVisits(this.props.state, this.props.publisherKey) + const updateWait = ledgerUtil.getRemainingRequiredTime(this.props.state, this.props.publisherKey) + if (!this.mounted || !shouldSetTimeout) { + return + } + let updateTimeout = setTimeout(() => { + appActions.onPublisherToggleUpdate() + }, updateWait) + this.setState({updateTimeout: updateTimeout}) + } + + clearUpdateTimeout () { + this.state && clearTimeout(this.state.updateTimeout) + } + + componentDidMount () { + this.mounted = true + if (!this.props.isVisibleInLedger) { + this.setUpdateTimeout() + } + } + + componentWillUnmount () { + this.mounted = false + this.clearUpdateTimeout() + } + + componentWillReceiveProps (newProps) { + if (!this.mounted) { + return + } + if ( + !newProps.isVisibleInLedger && + ( + newProps.location !== this.props.location || + newProps.publisherKey !== this.props.publisherKey + ) + ) { + this.clearUpdateTimeout() + this.setUpdateTimeout() + } + } + mergeProps (state, ownProps) { const currentWindow = state.get('currentWindow') const activeFrame = frameStateUtil.getActiveFrame(currentWindow) || Immutable.Map() @@ -68,8 +113,10 @@ class PublisherToggle extends React.Component { const props = {} // used in renderer + props.state = state props.tabId = tabId props.location = location + props.publisherKey = publisherKey props.isVisibleInLedger = ledgerUtil.visibleP(state, publisherKey) props.isEnabledForPaymentsPublisher = ledgerUtil.stickyP(state, publisherKey) props.isVerifiedPublisher = ledgerState.getPublisherOption(state, publisherKey, 'verified') diff --git a/js/actions/appActions.js b/js/actions/appActions.js index 7d5875b8ab6..aa792edbde2 100644 --- a/js/actions/appActions.js +++ b/js/actions/appActions.js @@ -1985,6 +1985,12 @@ const appActions = { dispatch({ actionType: appConstants.APP_ON_LEDGER_BACKUP_SUCCESS }) + }, + + onPublisherToggleUpdate: function () { + dispatch({ + actionType: appConstants.APP_ON_PUBLISHER_TOGGLE_UPDATE + }) } } diff --git a/js/constants/appConstants.js b/js/constants/appConstants.js index d050e07ac33..93596a95ec3 100644 --- a/js/constants/appConstants.js +++ b/js/constants/appConstants.js @@ -200,7 +200,8 @@ const appConstants = { APP_ON_REFERRAL_ACTIVITY: _, APP_ON_LEDGER_MEDIA_PUBLISHER: _, APP_ON_LEDGER_BACKUP_SUCCESS: _, - APP_ADD_PUBLISHER_TO_LEDGER: _ + APP_ADD_PUBLISHER_TO_LEDGER: _, + APP_ON_PUBLISHER_TOGGLE_UPDATE: _ } module.exports = mapValuesByKeys(appConstants) diff --git a/test/unit/app/common/lib/ledgerUtilTest.js b/test/unit/app/common/lib/ledgerUtilTest.js index 422fbea52df..e28f882fda6 100644 --- a/test/unit/app/common/lib/ledgerUtilTest.js +++ b/test/unit/app/common/lib/ledgerUtilTest.js @@ -7,6 +7,9 @@ const settings = require('../../../../../js/constants/settings') const ledgerMediaProviders = require('../../../../../app/common/constants/ledgerMediaProviders') const twitchEvents = require('../../../../../app/common/constants/twitchEvents') +const defaultState = Immutable.fromJS({ + ledger: {} +}) const baseState = Immutable.fromJS({ cache: { ledgerVideos: {} @@ -31,6 +34,8 @@ describe('ledgerUtil unit test', function () { const fakeAdBlock = require('../../../lib/fakeAdBlock') // settings + let paymentsMinVisits + let paymentsMinVisitTime let paymentsContributionAmount = 25 before(function () { @@ -52,6 +57,10 @@ describe('ledgerUtil unit test', function () { mockery.registerMock('../../../js/settings', { getSetting: (settingKey) => { switch (settingKey) { + case settings.PAYMENTS_MINIMUM_VISITS: + return paymentsMinVisits + case settings.PAYMENTS_MINIMUM_VISIT_TIME: + return paymentsMinVisitTime case settings.PAYMENTS_CONTRIBUTION_AMOUNT: return paymentsContributionAmount } @@ -1083,4 +1092,89 @@ describe('ledgerUtil unit test', function () { assert.deepEqual(result, 0) }) }) + + describe('hasRequiredVisits', function () { + it('returns true if minimum visits is set to 1', function () { + paymentsMinVisits = 1 + const publisherKey = 'brave.com' + + const result = ledgerUtil.hasRequiredVisits(defaultState, publisherKey) + assert(result) + }) + + it('returns false if the publisher is new and minimum visits is set to > 1', function () { + paymentsMinVisits = 5 + const publisherKey = 'new.com' + + const result = ledgerUtil.hasRequiredVisits(defaultState, publisherKey) + assert.equal(result, false) + }) + + it('returns true if the publisher is new and minimum visits is set to 1', function () { + paymentsMinVisits = 1 + const publisherKey = 'new.com' + + const result = ledgerUtil.hasRequiredVisits(defaultState, publisherKey) + assert.equal(result, true) + }) + + it('returns false if the publisher is > 1 visit away from minimum visits', function () { + paymentsMinVisits = 5 + const publisherVisits = 3 + const publisherKey = 'brave.com' + + const state = defaultState + .setIn(['ledger', 'synopsis', 'publishers', publisherKey, 'visits'], publisherVisits) + + const result = ledgerUtil.hasRequiredVisits(state, publisherKey) + assert.equal(result, false) + }) + + it('returns true if the publisher is 1 visit away from minimum visits', function () { + paymentsMinVisits = 5 + const publisherVisits = 4 + const publisherKey = 'brave.com' + + const state = defaultState + .setIn(['ledger', 'synopsis', 'publishers', publisherKey, 'visits'], publisherVisits) + + const result = ledgerUtil.hasRequiredVisits(state, publisherKey) + assert.equal(result, true) + }) + }) + + describe('getRemainingRequiredTime', function () { + it('returns the minimum visit time if the publisher is new', function () { + paymentsMinVisitTime = 8000 + const publisherKey = 'brave.com' + + const result = ledgerUtil.getRemainingRequiredTime(defaultState, publisherKey) + assert.equal(result, paymentsMinVisitTime) + }) + + it('returns the minimum visit time if the publisher has duration >= minimum visit time', function () { + paymentsMinVisitTime = 8000 + const publisherDuration = 8888 + const publisherKey = 'brave.com' + + const state = defaultState + .setIn(['ledger', 'synopsis', 'publishers', publisherKey, 'duration'], publisherDuration) + + const result = ledgerUtil.getRemainingRequiredTime(state, publisherKey) + assert.equal(result, paymentsMinVisitTime) + }) + + it('returns the difference in time if the publisher has duration < minimum visit time', function () { + paymentsMinVisitTime = 8000 + const expectedResult = 1500 + const publisherDuration = 6500 + const publisherKey = 'brave.com' + + const state = defaultState + .setIn(['ledger', 'synopsis', 'publishers', publisherKey, 'duration'], publisherDuration) + + const result = ledgerUtil.getRemainingRequiredTime(state, publisherKey) + assert.equal(result, expectedResult) + }) + }) })