From 22bfe9f62536ef8ce838eb130cfc3a3b53c6f403 Mon Sep 17 00:00:00 2001 From: DianaXWiki Date: Tue, 17 Mar 2026 12:59:53 +0200 Subject: [PATCH 01/12] Fix crowdfunding banner UI and show logic to a more advanced algorithm using users actions --- customize.dist/messages.js | 2 + www/common/common-icons.js | 4 +- www/common/common-ui-elements.js | 88 +++++++++++++++++++++---------- www/common/outer/local-store.js | 6 +-- www/common/sframe-common-outer.js | 63 ++++++++++++++++++++++ www/common/sframe-common.js | 4 +- www/logout/main.js | 8 +++ 7 files changed, 141 insertions(+), 34 deletions(-) diff --git a/customize.dist/messages.js b/customize.dist/messages.js index 06268dfd3..ffb00e3ef 100755 --- a/customize.dist/messages.js +++ b/customize.dist/messages.js @@ -134,6 +134,8 @@ define(req, function(AppConfig, Default, Language) { } }; + /// XXX + Messages.crowdfunding_subscribe = "Subscribe"; return Messages; }); diff --git a/www/common/common-icons.js b/www/common/common-icons.js index 84ad8c238..56a9a776c 100644 --- a/www/common/common-icons.js +++ b/www/common/common-icons.js @@ -250,7 +250,9 @@ define([ "badge-error": "circle-alert", // Other "maintenance": "construction", - "release-notes": "notepad-text" + "release-notes": "notepad-text", + "subscribe": "heart-handshake", + "donate": "hand-heart", }; Icons.add = (newIcons) => { diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index a559d7fc5..ae8a4145e 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -944,6 +944,7 @@ define([ return void UI.warn(Messages.autostore_error); } $(document).trigger('cpPadStored'); + common.getSframeChannel().query('Q_INCREMENT_CROWDFUNDING_ACTION', null, function () {}); UI.log(Messages.autostore_saved); }); }); @@ -3390,54 +3391,82 @@ define([ var priv = common.getMetadataMgr().getPrivateData(); if (priv.app === 'form' && !priv.canEdit && !priv.form_auditorKey) { return; } - var todo = function () { + var todo = function (actionCount) { crowdfundingState = true; - // Display the popup - var text = Messages.crowdfunding_popup_text; - var yes = h('button.cp-corner-primary', [ - Icons.get('external-link'), - 'OpenCollective' - ]); - var no = h('button.cp-corner-cancel', Messages.crowdfunding_popup_no); - var actions = h('div', [no, yes]); - + common.getSframeChannel().query('Q_RECORD_CROWDFUNDING_SHOWN', { count: actionCount }, function () {}); var dontShowAgain = function () { common.setAttribute(['general', 'crowdfunding'], false); Feedback.send('CROWDFUNDING_NEVER'); }; - var modal = UI.cornerPopup(text, actions, '', { - big: true, - alt: true, - dontShowAgain: dontShowAgain + var msgEl = h('div.msg'); + UI.setHTML(msgEl, Messages.crowdfunding_popup_text); + var content = h('div', [msgEl]); + var buttons = [ + { + name: Messages.crowdfunding_popup_no, + className: 'cancel', + onClick: function () { + Feedback.send('CROWDFUNDING_NO'); + } + } + ]; + if (Config.accounts_api) { + buttons.push({ + name: Messages.crowdfunding_subscribe, + className: 'primary', + iconClass: 'subscribe', + onClick: function () { + common.openURL('/accounts/'); + Feedback.send('CROWDFUNDING_SUBSCRIBE'); + } + }); + } + if (!Config.removeDonateButton) { + buttons.push({ + name: 'OpenCollective', + className: 'primary', + iconClass: 'donate', + onClick: function () { + common.openURL(priv.accounts.donateURL); + Feedback.send('CROWDFUNDING_YES'); + } + }); + } + buttons.push({ + name: Messages.dontShowAgain, + className: 'cancel left', + iconClass: 'close', + onClick: function () { + dontShowAgain(); + } }); - - $(yes).click(function () { - modal.delete(); - common.openURL(priv.accounts.donateURL); - Feedback.send('CROWDFUNDING_YES'); - }); - $(no).click(function () { - modal.delete(); - Feedback.send('CROWDFUNDING_NO'); + var modal = UI.dialog.customModal(content, { + scrollable: true, + rawContent: true, + buttons: buttons }); + UI.openCustomModal(modal, { wide: true }); }; if (force) { crowdfundingState = true; - return void todo(); + return void todo(0); } if (AppConfig.disableCrowdfundingMessages) { return; } if (priv.plan) { return; } + if (Config.removeDonateButton && !Config.accounts_api) { return; } crowdfundingState = true; common.getAttribute(['general', 'crowdfunding'], function (err, val) { - if (err || val === false) { return; } - common.getSframeChannel().query('Q_GET_PINNED_USAGE', null, function (err, obj) { - var quotaMb = obj.quota / (1024 * 1024); - if (quotaMb < 10) { return; } - todo(); + if (err || val === false) { crowdfundingState = false; return; } + common.getSframeChannel().query('Q_CROWDFUNDING_SHOULD_SHOW', null, function (err, result) { + if (err || !result || !result.show) { + crowdfundingState = false; + return; + } + todo(result.actionCount); }); }); }; @@ -3507,6 +3536,7 @@ define([ return void UI.warn(Messages.autostore_error); } $(document).trigger('cpPadStored'); + common.getSframeChannel().query('Q_INCREMENT_CROWDFUNDING_ACTION', null, function () {}); delete autoStoreModal[priv.channel]; modal.delete(); UI.log(Messages.autostore_saved); diff --git a/www/common/outer/local-store.js b/www/common/outer/local-store.js index a260f3def..02db01f5b 100644 --- a/www/common/outer/local-store.js +++ b/www/common/outer/local-store.js @@ -151,9 +151,9 @@ define([ sessionStorage.clear(); try { Object.keys(localStorage || {}).forEach(function (k) { - // Remvoe everything in localStorage except CACHE and FS_hash - if (/^CRYPTPAD_CACHE/.test(k) || /^LESS_CACHE/.test(k) || k === Constants.fileHashKey || /^CRYPTPAD_STORE|colortheme/.test(k)) { return; } - delete localStorage[k]; + // Remove everything in localStorage except CACHE and FS_hash + if (/^CRYPTPAD_CACHE/.test(k) || /^LESS_CACHE/.test(k) || k === Constants.fileHashKey || /^CRYPTPAD_STORE|colortheme/.test(k) || /^cp_crowdfunding_/.test(k)) { return; } + localStorage.removeItem(k); }); } catch (e) { console.error(e); } LocalStore.clearThumbnail(); diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 89fae9535..e96680838 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -1017,6 +1017,63 @@ define([ cb({error:e}); }); }); + var CROWDFUNDING_PREFIX = 'cp_crowdfunding_'; + var CROWDFUNDING_LEGACY = { visitCount: 'cp_crowdfundingVisitCount', firstSeen: 'cp_crowdfundingFirstSeen', lastShownAtCount: 'cp_crowdfundingLastShownAtCount', lastShownAtTime: 'cp_crowdfundingLastShownAtTime' }; + var crowdfundingKey = function (suffix) { + return CROWDFUNDING_PREFIX + suffix; + }; + var crowdfundingGet = function (suffix) { + var k = crowdfundingKey(suffix); + var val = localStorage.getItem(k); + if (val !== null && val !== '') return val; + var legacy = CROWDFUNDING_LEGACY[suffix]; + if (legacy) { + val = localStorage.getItem(legacy); + if (val !== null && val !== '') { + try { localStorage.setItem(k, val); localStorage.removeItem(legacy); } catch (e) {} + return val; + } + } + return null; + }; + var CROWDFUNDING_MIN_ACTIONS = 5; + var CROWDFUNDING_ACTIONS_INTERVAL = 10; + var CROWDFUNDING_MIN_QUOTA_MB = 10; + var CROWDFUNDING_ONE_DAY_MS = 24 * 60 * 60 * 1000; + + sframeChan.on('Q_CROWDFUNDING_SHOULD_SHOW', function (data, cb) { + Cryptpad.getPinnedUsage({}, function (e, used) { + if (e || typeof used !== 'number' || (used / (1024 * 1024)) < CROWDFUNDING_MIN_QUOTA_MB) { + return cb({ show: false }); + } + var actionCount = Number(crowdfundingGet('visitCount')) || 0; + var lastShownAtCount = Number(crowdfundingGet('lastShownAtCount')) || 0; + var lastShownAtTime = Number(crowdfundingGet('lastShownAtTime')) || 0; + var now = Date.now(); + var nextThreshold = lastShownAtCount === 0 ? CROWDFUNDING_MIN_ACTIONS : lastShownAtCount + CROWDFUNDING_ACTIONS_INTERVAL; + var enoughTimePassed = lastShownAtTime === 0 || (now - lastShownAtTime >= CROWDFUNDING_ONE_DAY_MS); + cb({ + show: actionCount >= nextThreshold && enoughTimePassed, + actionCount: actionCount + }); + }); + }); + sframeChan.on('Q_RECORD_CROWDFUNDING_SHOWN', function (data, cb) { + try { + if (data && typeof data.count === 'number') localStorage.setItem(crowdfundingKey('lastShownAtCount'), String(data.count)); + localStorage.setItem(crowdfundingKey('lastShownAtTime'), String(Date.now())); + } catch (e) { console.warn('crowdfunding write failed', e); } + cb(); + }); + sframeChan.on('Q_INCREMENT_CROWDFUNDING_ACTION', function (data, cb) { + try { + var k = crowdfundingKey('visitCount'); + var count = (Number(localStorage.getItem(k)) || 0) + 1; + localStorage.setItem(k, String(count)); + if (count === 1) localStorage.setItem(crowdfundingKey('firstSeen'), String(Date.now())); + } catch (e) { console.warn('crowdfunding write failed', e); } + cb(); + }); Cryptpad.mailbox.onEvent.reg(function (data, cb) { sframeChan.query('EV_MAILBOX_EVENT', data, function (err, obj) { @@ -2320,6 +2377,12 @@ define([ sframeChan.on('Q_CREATE_PAD', function (data, cb) { if (!isNewFile || rtStarted) { return; } + try { + var k = 'cp_crowdfunding_visitCount'; + var count = (Number(localStorage.getItem(k)) || 0) + 1; + localStorage.setItem(k, String(count)); + if (count === 1) localStorage.setItem('cp_crowdfunding_firstSeen', String(Date.now())); + } catch (e) { console.warn('crowdfunding write failed', e); } let feedbackKey = 'APP_' + parsed.type.toUpperCase() + '_CREATE'; Utils.Feedback.send(feedbackKey); // Create a new hash diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 320140d9c..b498d9603 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -1065,7 +1065,9 @@ define([ funcs.isPadStored(function (err, val) { if (err || !val) { return; } - UIElements.displayCrowdfunding(funcs); + ctx.sframeChan.query('Q_INCREMENT_CROWDFUNDING_ACTION', null, function () { + UIElements.displayCrowdfunding(funcs); + }); }); ctx.sframeChan.ready(); diff --git a/www/logout/main.js b/www/logout/main.js index 0e1d31664..eda0d2d47 100644 --- a/www/logout/main.js +++ b/www/logout/main.js @@ -8,7 +8,15 @@ define([ '/components/nthen/index.js', ], function (localForage, Cache, nThen) { nThen(function (w) { + // Preserve crowdfunding keys across logout so counters survive session changes + var crowdfundingEntries = Object.keys(localStorage).reduce(function (acc, k) { + if (/^cp_crowdfunding_/.test(k)) { acc[k] = localStorage.getItem(k); } + return acc; + }, {}); localStorage.clear(); + Object.keys(crowdfundingEntries).forEach(function (k) { + try { localStorage.setItem(k, crowdfundingEntries[k]); } catch (e) {} + }); localForage.clear(w()); Cache.clear(w()); }).nThen(function () { From 2b921a9d782ae0bd4899d1d23c31b15bceaa1059 Mon Sep 17 00:00:00 2001 From: DianaXWiki Date: Mon, 23 Mar 2026 17:23:07 +0200 Subject: [PATCH 02/12] Fix guest drive icons in grid view --- customize.dist/src/less2/include/drive.less | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/customize.dist/src/less2/include/drive.less b/customize.dist/src/less2/include/drive.less index 4982d81ce..b17a61385 100644 --- a/customize.dist/src/less2/include/drive.less +++ b/customize.dist/src/less2/include/drive.less @@ -976,6 +976,12 @@ stroke-width: 3.5; vector-effect: non-scaling-stroke; } + &.cp-app-drive-content-grid { + li.cp-app-drive-element-row > svg.lucide * { + stroke-width: 3.5; + vector-effect: non-scaling-stroke; + } + } } #cp-app-drive-new-ghost-dialog.cp-modal-container { From f44c15ac83e7d4e5ba8488db48b8f23577ddeab4 Mon Sep 17 00:00:00 2001 From: DianaXWiki Date: Mon, 23 Mar 2026 18:40:20 +0200 Subject: [PATCH 03/12] Remove subscribe button for guests --- www/common/common-ui-elements.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index ae8a4145e..ffee63b10 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -3411,7 +3411,7 @@ define([ } } ]; - if (Config.accounts_api) { + if (Config.accounts_api && priv.accountName) { buttons.push({ name: Messages.crowdfunding_subscribe, className: 'primary', From 233b930dd008c25eb26072905627d976d32cc188 Mon Sep 17 00:00:00 2001 From: DianaXWiki Date: Mon, 23 Mar 2026 18:41:38 +0200 Subject: [PATCH 04/12] Check pinned storage only for logged in users --- www/common/sframe-common-outer.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index e96680838..be91b17c5 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -1042,10 +1042,7 @@ define([ var CROWDFUNDING_ONE_DAY_MS = 24 * 60 * 60 * 1000; sframeChan.on('Q_CROWDFUNDING_SHOULD_SHOW', function (data, cb) { - Cryptpad.getPinnedUsage({}, function (e, used) { - if (e || typeof used !== 'number' || (used / (1024 * 1024)) < CROWDFUNDING_MIN_QUOTA_MB) { - return cb({ show: false }); - } + var check = function () { var actionCount = Number(crowdfundingGet('visitCount')) || 0; var lastShownAtCount = Number(crowdfundingGet('lastShownAtCount')) || 0; var lastShownAtTime = Number(crowdfundingGet('lastShownAtTime')) || 0; @@ -1056,6 +1053,13 @@ define([ show: actionCount >= nextThreshold && enoughTimePassed, actionCount: actionCount }); + }; + if (CROWDFUNDING_MIN_QUOTA_MB <= 0) { return check(); } + Cryptpad.getPinnedUsage({}, function (e, used) { + if (e || typeof used !== 'number' || (used / (1024 * 1024)) < CROWDFUNDING_MIN_QUOTA_MB) { + return cb({ show: false }); + } + check(); }); }); sframeChan.on('Q_RECORD_CROWDFUNDING_SHOWN', function (data, cb) { From 87972e95c688e81bd37c519b8a7be3daf8872254 Mon Sep 17 00:00:00 2001 From: DianaXWiki Date: Wed, 1 Apr 2026 17:58:09 +0300 Subject: [PATCH 05/12] Implement review changes regarding UI --- customize.dist/messages.js | 5 ++++- www/common/common-icons.js | 2 +- www/common/common-ui-elements.js | 3 ++- www/common/sframe-common.js | 7 ++----- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/customize.dist/messages.js b/customize.dist/messages.js index ffb00e3ef..ac0f60fe1 100755 --- a/customize.dist/messages.js +++ b/customize.dist/messages.js @@ -135,7 +135,10 @@ define(req, function(AppConfig, Default, Language) { }; /// XXX - Messages.crowdfunding_subscribe = "Subscribe"; + // To be edited for the Spring Release + // Messages.dontShowAgain = "Don't show again"; + // Messages.crowdfunding_popup_text = "

We need your help!

To ensure that CryptPad is actively developed, consider supporting the project via the OpenCollective page, where you can see our Roadmap and Funding goals." + // Messages.crowdfunding_popup_text2 = "Alternatively, we have subscription plans available on CryptPad.fr." return Messages; }); diff --git a/www/common/common-icons.js b/www/common/common-icons.js index 56a9a776c..c43cc70a8 100644 --- a/www/common/common-icons.js +++ b/www/common/common-icons.js @@ -251,8 +251,8 @@ define([ // Other "maintenance": "construction", "release-notes": "notepad-text", - "subscribe": "heart-handshake", "donate": "hand-heart", + "snooze": "timer", }; Icons.add = (newIcons) => { diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index ffee63b10..4b3f99f21 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -3406,6 +3406,7 @@ define([ { name: Messages.crowdfunding_popup_no, className: 'cancel', + iconClass: 'snooze', onClick: function () { Feedback.send('CROWDFUNDING_NO'); } @@ -3413,7 +3414,7 @@ define([ ]; if (Config.accounts_api && priv.accountName) { buttons.push({ - name: Messages.crowdfunding_subscribe, + name: Messages.features_f_subscribe, className: 'primary', iconClass: 'subscribe', onClick: function () { diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index b498d9603..3a33e576b 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -1063,11 +1063,8 @@ define([ document.title = title; }); - funcs.isPadStored(function (err, val) { - if (err || !val) { return; } - ctx.sframeChan.query('Q_INCREMENT_CROWDFUNDING_ACTION', null, function () { - UIElements.displayCrowdfunding(funcs); - }); + ctx.sframeChan.query('Q_INCREMENT_CROWDFUNDING_ACTION', null, function () { + UIElements.displayCrowdfunding(funcs); }); ctx.sframeChan.ready(); From 6c8070bdeeda84533f3281ecf5f348d868596b7a Mon Sep 17 00:00:00 2001 From: DianaXWiki Date: Tue, 14 Apr 2026 17:51:38 +0300 Subject: [PATCH 06/12] Add the logic separation between logged in and guests metric handling + not count form readonly + dedupe for duplicate increments --- www/common/common-icons.js | 5 +- www/common/common-ui-elements.js | 12 ++- www/common/sframe-common-outer.js | 168 +++++++++++++++++++++--------- www/common/sframe-common.js | 22 +++- 4 files changed, 145 insertions(+), 62 deletions(-) diff --git a/www/common/common-icons.js b/www/common/common-icons.js index c43cc70a8..74865b4f9 100644 --- a/www/common/common-icons.js +++ b/www/common/common-icons.js @@ -251,8 +251,9 @@ define([ // Other "maintenance": "construction", "release-notes": "notepad-text", - "donate": "hand-heart", - "snooze": "timer", + "crowdfunding-donate": "hand-heart", + "crowdfunding-snooze": "timer", + "crowdfunding-donate2": "ticket" }; Icons.add = (newIcons) => { diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 4b3f99f21..d3d784c32 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -944,7 +944,6 @@ define([ return void UI.warn(Messages.autostore_error); } $(document).trigger('cpPadStored'); - common.getSframeChannel().query('Q_INCREMENT_CROWDFUNDING_ACTION', null, function () {}); UI.log(Messages.autostore_saved); }); }); @@ -3389,7 +3388,11 @@ define([ UIElements.displayCrowdfunding = function (common, force) { if (crowdfundingState) { return; } var priv = common.getMetadataMgr().getPrivateData(); - if (priv.app === 'form' && !priv.canEdit && !priv.form_auditorKey) { return; } + var inDrive = /^\/drive/; + var isDriveContext = inDrive.test(priv.pathname || ''); + if (isDriveContext) { return; } + if (!priv.channel) { return; } + if (priv.app === 'form' && priv.readOnly && !priv.form_auditorHash && !priv.form_auditorKey) { return; } var todo = function (actionCount) { crowdfundingState = true; @@ -3416,7 +3419,7 @@ define([ buttons.push({ name: Messages.features_f_subscribe, className: 'primary', - iconClass: 'subscribe', + iconClass: 'crowdfunding-donate2', onClick: function () { common.openURL('/accounts/'); Feedback.send('CROWDFUNDING_SUBSCRIBE'); @@ -3487,7 +3490,7 @@ define([ // This pad will be deleted automatically, it shouldn't be stored if (priv.burnAfterReading) { return; } - if (priv.app === 'form' && !priv.canEdit && !priv.form_auditorKey && !common.isLoggedIn()) { return; } + if (priv.app === 'form' && priv.readOnly && !priv.form_auditorHash && !priv.form_auditorKey && !common.isLoggedIn()) { return; } var typeMsg = priv.pathname.indexOf('/file/') !== -1 ? Messages.autostore_file : priv.pathname.indexOf('/drive/') !== -1 ? Messages.autostore_sf : Messages.autostore_pad; @@ -3537,7 +3540,6 @@ define([ return void UI.warn(Messages.autostore_error); } $(document).trigger('cpPadStored'); - common.getSframeChannel().query('Q_INCREMENT_CROWDFUNDING_ACTION', null, function () {}); delete autoStoreModal[priv.channel]; modal.delete(); UI.log(Messages.autostore_saved); diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index be91b17c5..a7cbd6e4c 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -1018,65 +1018,135 @@ define([ }); }); var CROWDFUNDING_PREFIX = 'cp_crowdfunding_'; - var CROWDFUNDING_LEGACY = { visitCount: 'cp_crowdfundingVisitCount', firstSeen: 'cp_crowdfundingFirstSeen', lastShownAtCount: 'cp_crowdfundingLastShownAtCount', lastShownAtTime: 'cp_crowdfundingLastShownAtTime' }; - var crowdfundingKey = function (suffix) { - return CROWDFUNDING_PREFIX + suffix; - }; - var crowdfundingGet = function (suffix) { - var k = crowdfundingKey(suffix); - var val = localStorage.getItem(k); - if (val !== null && val !== '') return val; - var legacy = CROWDFUNDING_LEGACY[suffix]; - if (legacy) { - val = localStorage.getItem(legacy); - if (val !== null && val !== '') { - try { localStorage.setItem(k, val); localStorage.removeItem(legacy); } catch (e) {} - return val; - } - } - return null; - }; - var CROWDFUNDING_MIN_ACTIONS = 5; - var CROWDFUNDING_ACTIONS_INTERVAL = 10; + var CROWDFUNDING_DRIVE_KEY = ['general', 'crowdfunding_metrics']; + var CROWDFUNDING_MIN_ACTIONS = 10; + var CROWDFUNDING_ACTIONS_INTERVAL = 15; var CROWDFUNDING_MIN_QUOTA_MB = 10; var CROWDFUNDING_ONE_DAY_MS = 24 * 60 * 60 * 1000; + var CROWDFUNDING_OPEN_DEDUPE_MS = 15000; + var crowdfundingLastOpenScope = null; + var crowdfundingLastOpenAt = 0; + var crowdfundingGetLS = function () { + var get = function (suffix) { + var k = CROWDFUNDING_PREFIX + suffix; + var val = localStorage.getItem(k); + if (val !== null && val !== '') { return Number(val) || null; } + return null; + }; + return { + visitCount: get('visitCount') || 0, + firstSeen: get('firstSeen') || null, + lastShownAtCount: get('lastShownAtCount') || 0, + lastShownAtTime: get('lastShownAtTime') || 0 + }; + }; + // Read metrics: encrypted drive for logged-in users, localStorage for guests + var crowdfundingReadMetrics = function (cb) { + if (!Utils.LocalStore.isLoggedIn()) { return cb(crowdfundingGetLS()); } + Cryptpad.getAttribute(CROWDFUNDING_DRIVE_KEY, function (e, metrics) { + if (e || !metrics || typeof metrics !== 'object') { + return cb(crowdfundingGetLS()); + } + cb(metrics); + }); + }; + // Write metrics: encrypted drive for logged-in users, localStorage for guests + var crowdfundingWriteMetrics = function (metrics, cb) { + if (!Utils.LocalStore.isLoggedIn()) { + try { + ['visitCount', 'firstSeen', 'lastShownAtCount', 'lastShownAtTime'].forEach(function (k) { + if (metrics[k] !== null && metrics[k] !== undefined) { + localStorage.setItem(CROWDFUNDING_PREFIX + k, String(metrics[k])); + } + }); + } catch (e) {} + return cb && cb(); + } + Cryptpad.setAttribute(CROWDFUNDING_DRIVE_KEY, metrics, function (e) { + if (!e) { + return cb && cb(); + } + try { + ['visitCount', 'firstSeen', 'lastShownAtCount', 'lastShownAtTime'].forEach(function (k) { + if (metrics[k] !== null && metrics[k] !== undefined) { + localStorage.setItem(CROWDFUNDING_PREFIX + k, String(metrics[k])); + } + }); + } catch (e) {} + cb && cb(); + }); + }; + var crowdfundingIncrementAction = function (cb) { + crowdfundingReadMetrics(function (metrics) { + metrics.visitCount = (metrics.visitCount || 0) + 1; + if (!metrics.firstSeen) { metrics.firstSeen = Date.now(); } + crowdfundingWriteMetrics(metrics, cb); + }); + }; sframeChan.on('Q_CROWDFUNDING_SHOULD_SHOW', function (data, cb) { - var check = function () { - var actionCount = Number(crowdfundingGet('visitCount')) || 0; - var lastShownAtCount = Number(crowdfundingGet('lastShownAtCount')) || 0; - var lastShownAtTime = Number(crowdfundingGet('lastShownAtTime')) || 0; + crowdfundingReadMetrics(function (metrics) { + var actionCount = metrics.visitCount || 0; + var lastShownAtCount = metrics.lastShownAtCount || 0; + var lastShownAtTime = metrics.lastShownAtTime || 0; var now = Date.now(); var nextThreshold = lastShownAtCount === 0 ? CROWDFUNDING_MIN_ACTIONS : lastShownAtCount + CROWDFUNDING_ACTIONS_INTERVAL; var enoughTimePassed = lastShownAtTime === 0 || (now - lastShownAtTime >= CROWDFUNDING_ONE_DAY_MS); - cb({ - show: actionCount >= nextThreshold && enoughTimePassed, - actionCount: actionCount - }); - }; - if (CROWDFUNDING_MIN_QUOTA_MB <= 0) { return check(); } - Cryptpad.getPinnedUsage({}, function (e, used) { - if (e || typeof used !== 'number' || (used / (1024 * 1024)) < CROWDFUNDING_MIN_QUOTA_MB) { - return cb({ show: false }); + var showFromActions = actionCount >= nextThreshold && enoughTimePassed; + if (showFromActions) { + return cb({ + show: true, + actionCount: actionCount + }); } - check(); + if (CROWDFUNDING_MIN_QUOTA_MB <= 0) { + return cb({ + show: false, + actionCount: actionCount + }); + } + Cryptpad.getPinnedUsage({}, function (e, used) { + var usedMb = (typeof used === 'number') ? (used / (1024 * 1024)) : 0; + cb({ + show: !e && usedMb >= CROWDFUNDING_MIN_QUOTA_MB && enoughTimePassed, + actionCount: actionCount + }); + }); }); }); sframeChan.on('Q_RECORD_CROWDFUNDING_SHOWN', function (data, cb) { - try { - if (data && typeof data.count === 'number') localStorage.setItem(crowdfundingKey('lastShownAtCount'), String(data.count)); - localStorage.setItem(crowdfundingKey('lastShownAtTime'), String(Date.now())); - } catch (e) { console.warn('crowdfunding write failed', e); } - cb(); + crowdfundingReadMetrics(function (metrics) { + if (data && typeof data.count === 'number') { metrics.lastShownAtCount = data.count; } + metrics.lastShownAtTime = Date.now(); + crowdfundingWriteMetrics(metrics, cb); + }); }); - sframeChan.on('Q_INCREMENT_CROWDFUNDING_ACTION', function (data, cb) { + sframeChan.on('Q_CROWDFUNDING_INCREMENT_OPEN', function (data, cb) { + if (readOnly) { return cb && cb(); } + var requestedScope = data && data.scope; + var scope = (secret && secret.channel) || requestedScope; + if (!scope) { return cb && cb(); } + var now = Date.now(); + if (crowdfundingLastOpenScope === scope && + (now - crowdfundingLastOpenAt) < CROWDFUNDING_OPEN_DEDUPE_MS) { + return cb && cb(); + } try { - var k = crowdfundingKey('visitCount'); - var count = (Number(localStorage.getItem(k)) || 0) + 1; - localStorage.setItem(k, String(count)); - if (count === 1) localStorage.setItem(crowdfundingKey('firstSeen'), String(Date.now())); - } catch (e) { console.warn('crowdfunding write failed', e); } - cb(); + var dedupeRaw = localStorage.getItem(CROWDFUNDING_PREFIX + 'open_dedupe'); + var dedupe = dedupeRaw ? JSON.parse(dedupeRaw) : null; + if (dedupe && dedupe.scope === scope && + typeof dedupe.ts === 'number' && + (now - dedupe.ts) < CROWDFUNDING_OPEN_DEDUPE_MS) { + return cb && cb(); + } + localStorage.setItem(CROWDFUNDING_PREFIX + 'open_dedupe', JSON.stringify({ + scope: scope, + ts: now + })); + } catch (e) {} + crowdfundingLastOpenScope = scope; + crowdfundingLastOpenAt = now; + crowdfundingIncrementAction(cb); }); Cryptpad.mailbox.onEvent.reg(function (data, cb) { @@ -2381,12 +2451,6 @@ define([ sframeChan.on('Q_CREATE_PAD', function (data, cb) { if (!isNewFile || rtStarted) { return; } - try { - var k = 'cp_crowdfunding_visitCount'; - var count = (Number(localStorage.getItem(k)) || 0) + 1; - localStorage.setItem(k, String(count)); - if (count === 1) localStorage.setItem('cp_crowdfunding_firstSeen', String(Date.now())); - } catch (e) { console.warn('crowdfunding write failed', e); } let feedbackKey = 'APP_' + parsed.type.toUpperCase() + '_CREATE'; Utils.Feedback.send(feedbackKey); // Create a new hash diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 3a33e576b..212d87fb6 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -768,7 +768,7 @@ define([ // Ctrl+E: New pad modal var priv = ctx.metadataMgr.getPrivateData(); if (e.which === 69 && isApp) { - if (priv.app === 'form' && !priv.canEdit && !priv.form_auditorKey) { return; } + if (priv.app === 'form' && priv.readOnly && !priv.form_auditorHash && !priv.form_auditorKey) { return; } e.preventDefault(); return void funcs.createNewPadModal(); } @@ -1063,9 +1063,25 @@ define([ document.title = title; }); - ctx.sframeChan.query('Q_INCREMENT_CROWDFUNDING_ACTION', null, function () { + var showCrowdfunding = function () { UIElements.displayCrowdfunding(funcs); - }); + }; + var privateData = ctx.metadataMgr.getPrivateData(); + var inDrive = /^\/drive/; + var isDriveContext = inDrive.test(privateData.pathname || ''); + var isReadOnlyContext = Boolean(privateData.readOnly); + var isReadOnlyFormResponse = privateData.app === 'form' && + !privateData.form_auditorKey && + ((!privateData.canEdit) || + (isReadOnlyContext && !privateData.form_auditorHash)); + var openScope = privateData.channel; + if (isDriveContext || isReadOnlyFormResponse || !openScope) { + showCrowdfunding(); + } else { + ctx.sframeChan.query('Q_CROWDFUNDING_INCREMENT_OPEN', { + scope: openScope + }, showCrowdfunding); + } ctx.sframeChan.ready(); From 82852d1e527291cba096fcba48fb38a3b997d3dd Mon Sep 17 00:00:00 2001 From: DianaXWiki Date: Thu, 16 Apr 2026 16:19:46 +0300 Subject: [PATCH 07/12] Cleanup code + fixes --- www/common/common-ui-elements.js | 4 ++-- www/common/outer/local-store.js | 2 +- www/common/sframe-common-outer.js | 23 ++++++++++------------- www/logout/main.js | 8 -------- 4 files changed, 13 insertions(+), 24 deletions(-) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index d3d784c32..a6887db89 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -3396,7 +3396,6 @@ define([ var todo = function (actionCount) { crowdfundingState = true; - common.getSframeChannel().query('Q_RECORD_CROWDFUNDING_SHOWN', { count: actionCount }, function () {}); var dontShowAgain = function () { common.setAttribute(['general', 'crowdfunding'], false); Feedback.send('CROWDFUNDING_NEVER'); @@ -3409,7 +3408,7 @@ define([ { name: Messages.crowdfunding_popup_no, className: 'cancel', - iconClass: 'snooze', + iconClass: 'crowdfunding-snooze', onClick: function () { Feedback.send('CROWDFUNDING_NO'); } @@ -3451,6 +3450,7 @@ define([ buttons: buttons }); UI.openCustomModal(modal, { wide: true }); + common.getSframeChannel().query('Q_RECORD_CROWDFUNDING_SHOWN', { count: actionCount }, function () {}); }; if (force) { diff --git a/www/common/outer/local-store.js b/www/common/outer/local-store.js index 02db01f5b..9882d1fb0 100644 --- a/www/common/outer/local-store.js +++ b/www/common/outer/local-store.js @@ -152,7 +152,7 @@ define([ try { Object.keys(localStorage || {}).forEach(function (k) { // Remove everything in localStorage except CACHE and FS_hash - if (/^CRYPTPAD_CACHE/.test(k) || /^LESS_CACHE/.test(k) || k === Constants.fileHashKey || /^CRYPTPAD_STORE|colortheme/.test(k) || /^cp_crowdfunding_/.test(k)) { return; } + if (/^CRYPTPAD_CACHE/.test(k) || /^LESS_CACHE/.test(k) || k === Constants.fileHashKey || /^CRYPTPAD_STORE|colortheme/.test(k) || (!isDeletion && /^cp_crowdfunding_/.test(k))) { return; } localStorage.removeItem(k); }); } catch (e) { console.error(e); } diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index a7cbd6e4c..1576be039 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -1045,7 +1045,12 @@ define([ if (!Utils.LocalStore.isLoggedIn()) { return cb(crowdfundingGetLS()); } Cryptpad.getAttribute(CROWDFUNDING_DRIVE_KEY, function (e, metrics) { if (e || !metrics || typeof metrics !== 'object') { - return cb(crowdfundingGetLS()); + return cb({ + visitCount: 0, + firstSeen: null, + lastShownAtCount: 0, + lastShownAtTime: 0 + }); } cb(metrics); }); @@ -1062,17 +1067,7 @@ define([ } catch (e) {} return cb && cb(); } - Cryptpad.setAttribute(CROWDFUNDING_DRIVE_KEY, metrics, function (e) { - if (!e) { - return cb && cb(); - } - try { - ['visitCount', 'firstSeen', 'lastShownAtCount', 'lastShownAtTime'].forEach(function (k) { - if (metrics[k] !== null && metrics[k] !== undefined) { - localStorage.setItem(CROWDFUNDING_PREFIX + k, String(metrics[k])); - } - }); - } catch (e) {} + Cryptpad.setAttribute(CROWDFUNDING_DRIVE_KEY, metrics, function () { cb && cb(); }); }; @@ -1143,7 +1138,9 @@ define([ scope: scope, ts: now })); - } catch (e) {} + } catch (e) { + try { localStorage.removeItem(CROWDFUNDING_PREFIX + 'open_dedupe'); } catch (e2) {} + } crowdfundingLastOpenScope = scope; crowdfundingLastOpenAt = now; crowdfundingIncrementAction(cb); diff --git a/www/logout/main.js b/www/logout/main.js index eda0d2d47..0e1d31664 100644 --- a/www/logout/main.js +++ b/www/logout/main.js @@ -8,15 +8,7 @@ define([ '/components/nthen/index.js', ], function (localForage, Cache, nThen) { nThen(function (w) { - // Preserve crowdfunding keys across logout so counters survive session changes - var crowdfundingEntries = Object.keys(localStorage).reduce(function (acc, k) { - if (/^cp_crowdfunding_/.test(k)) { acc[k] = localStorage.getItem(k); } - return acc; - }, {}); localStorage.clear(); - Object.keys(crowdfundingEntries).forEach(function (k) { - try { localStorage.setItem(k, crowdfundingEntries[k]); } catch (e) {} - }); localForage.clear(w()); Cache.clear(w()); }).nThen(function () { From b17d9c4ca0075b58f2ffbfbf9c338dd09c81aee5 Mon Sep 17 00:00:00 2001 From: DianaXWiki Date: Thu, 16 Apr 2026 16:41:21 +0300 Subject: [PATCH 08/12] Styling --- www/common/sframe-common.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 212d87fb6..ca9951b6d 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -1070,10 +1070,7 @@ define([ var inDrive = /^\/drive/; var isDriveContext = inDrive.test(privateData.pathname || ''); var isReadOnlyContext = Boolean(privateData.readOnly); - var isReadOnlyFormResponse = privateData.app === 'form' && - !privateData.form_auditorKey && - ((!privateData.canEdit) || - (isReadOnlyContext && !privateData.form_auditorHash)); + var isReadOnlyFormResponse = privateData.app === 'form' && !privateData.form_auditorKey && ((!privateData.canEdit) || (isReadOnlyContext && !privateData.form_auditorHash)); var openScope = privateData.channel; if (isDriveContext || isReadOnlyFormResponse || !openScope) { showCrowdfunding(); From 87405e77e27ef432bff1f6b2cb7b24d331321410 Mon Sep 17 00:00:00 2001 From: DianaXWiki Date: Thu, 16 Apr 2026 16:47:58 +0300 Subject: [PATCH 09/12] Keep styling --- www/common/outer/local-store.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/common/outer/local-store.js b/www/common/outer/local-store.js index 9882d1fb0..cd14dc3b1 100644 --- a/www/common/outer/local-store.js +++ b/www/common/outer/local-store.js @@ -153,7 +153,7 @@ define([ Object.keys(localStorage || {}).forEach(function (k) { // Remove everything in localStorage except CACHE and FS_hash if (/^CRYPTPAD_CACHE/.test(k) || /^LESS_CACHE/.test(k) || k === Constants.fileHashKey || /^CRYPTPAD_STORE|colortheme/.test(k) || (!isDeletion && /^cp_crowdfunding_/.test(k))) { return; } - localStorage.removeItem(k); + delete localStorage[k]; }); } catch (e) { console.error(e); } LocalStore.clearThumbnail(); From 1f1b5ae939bfbb633c87b4c46024b40c0c86649d Mon Sep 17 00:00:00 2001 From: DianaXWiki Date: Thu, 16 Apr 2026 20:04:40 +0300 Subject: [PATCH 10/12] Eliminate scope handling + feedback implementation --- www/common/common-ui-elements.js | 8 ++++---- www/common/sframe-common-outer.js | 34 +++++-------------------------- www/common/sframe-common.js | 16 +++++---------- www/unsafeiframe/main.js | 1 + 4 files changed, 15 insertions(+), 44 deletions(-) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index a6887db89..ba4095b72 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -3394,7 +3394,7 @@ define([ if (!priv.channel) { return; } if (priv.app === 'form' && priv.readOnly && !priv.form_auditorHash && !priv.form_auditorKey) { return; } - var todo = function (actionCount) { + var todo = function () { crowdfundingState = true; var dontShowAgain = function () { common.setAttribute(['general', 'crowdfunding'], false); @@ -3450,12 +3450,11 @@ define([ buttons: buttons }); UI.openCustomModal(modal, { wide: true }); - common.getSframeChannel().query('Q_RECORD_CROWDFUNDING_SHOWN', { count: actionCount }, function () {}); }; if (force) { crowdfundingState = true; - return void todo(0); + return void todo(); } if (AppConfig.disableCrowdfundingMessages) { return; } @@ -3470,7 +3469,8 @@ define([ crowdfundingState = false; return; } - todo(result.actionCount); + todo(); + common.getSframeChannel().query('Q_RECORD_CROWDFUNDING_SHOWN', {}, function () {}); }); }); }; diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 1576be039..781cfe5f3 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -1019,13 +1019,14 @@ define([ }); var CROWDFUNDING_PREFIX = 'cp_crowdfunding_'; var CROWDFUNDING_DRIVE_KEY = ['general', 'crowdfunding_metrics']; + // First action count threshold before showing the banner (10 actions) var CROWDFUNDING_MIN_ACTIONS = 10; + // Additional actions required after each shown banner (15 actions) var CROWDFUNDING_ACTIONS_INTERVAL = 15; + // Quota usage threshold for quota-based banner display (10 MB) var CROWDFUNDING_MIN_QUOTA_MB = 10; + // Cooldown between banner displays based on last shown timestamp (24 hours) var CROWDFUNDING_ONE_DAY_MS = 24 * 60 * 60 * 1000; - var CROWDFUNDING_OPEN_DEDUPE_MS = 15000; - var crowdfundingLastOpenScope = null; - var crowdfundingLastOpenAt = 0; var crowdfundingGetLS = function () { var get = function (suffix) { var k = CROWDFUNDING_PREFIX + suffix; @@ -1111,38 +1112,13 @@ define([ }); sframeChan.on('Q_RECORD_CROWDFUNDING_SHOWN', function (data, cb) { crowdfundingReadMetrics(function (metrics) { - if (data && typeof data.count === 'number') { metrics.lastShownAtCount = data.count; } + metrics.lastShownAtCount = (data && typeof data.count === 'number') ? data.count : (metrics.visitCount || 0); metrics.lastShownAtTime = Date.now(); crowdfundingWriteMetrics(metrics, cb); }); }); sframeChan.on('Q_CROWDFUNDING_INCREMENT_OPEN', function (data, cb) { if (readOnly) { return cb && cb(); } - var requestedScope = data && data.scope; - var scope = (secret && secret.channel) || requestedScope; - if (!scope) { return cb && cb(); } - var now = Date.now(); - if (crowdfundingLastOpenScope === scope && - (now - crowdfundingLastOpenAt) < CROWDFUNDING_OPEN_DEDUPE_MS) { - return cb && cb(); - } - try { - var dedupeRaw = localStorage.getItem(CROWDFUNDING_PREFIX + 'open_dedupe'); - var dedupe = dedupeRaw ? JSON.parse(dedupeRaw) : null; - if (dedupe && dedupe.scope === scope && - typeof dedupe.ts === 'number' && - (now - dedupe.ts) < CROWDFUNDING_OPEN_DEDUPE_MS) { - return cb && cb(); - } - localStorage.setItem(CROWDFUNDING_PREFIX + 'open_dedupe', JSON.stringify({ - scope: scope, - ts: now - })); - } catch (e) { - try { localStorage.removeItem(CROWDFUNDING_PREFIX + 'open_dedupe'); } catch (e2) {} - } - crowdfundingLastOpenScope = scope; - crowdfundingLastOpenAt = now; crowdfundingIncrementAction(cb); }); diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index ca9951b6d..ae78e8d20 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -1067,17 +1067,11 @@ define([ UIElements.displayCrowdfunding(funcs); }; var privateData = ctx.metadataMgr.getPrivateData(); - var inDrive = /^\/drive/; - var isDriveContext = inDrive.test(privateData.pathname || ''); - var isReadOnlyContext = Boolean(privateData.readOnly); - var isReadOnlyFormResponse = privateData.app === 'form' && !privateData.form_auditorKey && ((!privateData.canEdit) || (isReadOnlyContext && !privateData.form_auditorHash)); - var openScope = privateData.channel; - if (isDriveContext || isReadOnlyFormResponse || !openScope) { - showCrowdfunding(); - } else { - ctx.sframeChan.query('Q_CROWDFUNDING_INCREMENT_OPEN', { - scope: openScope - }, showCrowdfunding); + var isDriveContext = privateData.app === 'drive'; + var isReadOnlyFormResponse = privateData.app === 'form' && priv.readOnly && !priv.form_auditorHash && !priv.form_auditorKey; + var skipCrowdfunding = privateData.secureIframe === true || privateData.unsafeIframe === true || isReadOnlyFormResponse || isDriveContext; + if (!skipCrowdfunding) { + ctx.sframeChan.query('Q_CROWDFUNDING_INCREMENT_OPEN', showCrowdfunding); } ctx.sframeChan.ready(); diff --git a/www/unsafeiframe/main.js b/www/unsafeiframe/main.js index 0965b9c85..50e047c03 100644 --- a/www/unsafeiframe/main.js +++ b/www/unsafeiframe/main.js @@ -106,6 +106,7 @@ define([ origin: window.location.origin, pathname: window.location.pathname, feedbackAllowed: Utils.Feedback.state, + unsafeIframe: true, }; for (var k in additionalPriv) { metaObj.priv[k] = additionalPriv[k]; } From b9e332f84e1edf3310a375f75f76b7f6cc77e07c Mon Sep 17 00:00:00 2001 From: DianaXWiki Date: Thu, 16 Apr 2026 20:07:23 +0300 Subject: [PATCH 11/12] Fix drive determination --- www/common/common-ui-elements.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index ba4095b72..4f83fccd5 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -3388,9 +3388,7 @@ define([ UIElements.displayCrowdfunding = function (common, force) { if (crowdfundingState) { return; } var priv = common.getMetadataMgr().getPrivateData(); - var inDrive = /^\/drive/; - var isDriveContext = inDrive.test(priv.pathname || ''); - if (isDriveContext) { return; } + if (priv.app === 'drive') { return; } if (!priv.channel) { return; } if (priv.app === 'form' && priv.readOnly && !priv.form_auditorHash && !priv.form_auditorKey) { return; } From 0fd5edb929cde7d0ab003eb24d8a180ea2d6f9fb Mon Sep 17 00:00:00 2001 From: DianaXWiki Date: Fri, 17 Apr 2026 15:25:45 +0300 Subject: [PATCH 12/12] Fix small errors --- www/common/sframe-common.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index ae78e8d20..0e6d10fac 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -1068,10 +1068,10 @@ define([ }; var privateData = ctx.metadataMgr.getPrivateData(); var isDriveContext = privateData.app === 'drive'; - var isReadOnlyFormResponse = privateData.app === 'form' && priv.readOnly && !priv.form_auditorHash && !priv.form_auditorKey; + var isReadOnlyFormResponse = privateData.app === 'form' && privateData.readOnly && !privateData.form_auditorHash && !privateData.form_auditorKey; var skipCrowdfunding = privateData.secureIframe === true || privateData.unsafeIframe === true || isReadOnlyFormResponse || isDriveContext; if (!skipCrowdfunding) { - ctx.sframeChan.query('Q_CROWDFUNDING_INCREMENT_OPEN', showCrowdfunding); + ctx.sframeChan.query('Q_CROWDFUNDING_INCREMENT_OPEN', {}, showCrowdfunding); } ctx.sframeChan.ready();