From b9d57b2112de48f5a2a5396a3347d9366092960e Mon Sep 17 00:00:00 2001 From: danshuitaihejie <474182370@qq.com> Date: Wed, 22 May 2024 23:47:32 +0800 Subject: [PATCH 01/18] fix: avoid import action break subscription restriction --- src/components/app.jsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/components/app.jsx b/src/components/app.jsx index dccacf88..97abe97f 100644 --- a/src/components/app.jsx +++ b/src/components/app.jsx @@ -859,6 +859,10 @@ BookLibService.Borrow(id) { trackEvent('ui', LocalStorageKeys.LOGIN_AND_SAVE_MESSAGE_SEEN, 'local'); } var isNewItem = !this.state.currentItem.id; + if (isNewItem && this.checkItemsLimit()) { + this.proBtnClickHandler(); + return; + } this.state.currentItem.id = this.state.currentItem.id || 'item-' + generateRandomId(); await this.setState({ @@ -1300,6 +1304,14 @@ BookLibService.Borrow(id) { * Called from inside ask-to-import-modal */ importCreationsAndSettingsIntoApp() { + if (!this.checkItemsLimit()) { + mixpanel.track({ + event: 'Free Limit', + category: '3 diagrams limit', + label: 'ImportCreations', + }); + return; + } this.mergeImportedItems(this.oldSavedItems).then(() => { trackEvent('fn', 'oldItemsImported'); this.dontAskToImportAnymore(); From e9fd313fdca7a842dce07e2e225dfca21851664f Mon Sep 17 00:00:00 2001 From: danshuitaihejie <474182370@qq.com> Date: Sat, 29 Jun 2024 14:18:52 +0800 Subject: [PATCH 02/18] feat: check items limit when saving --- src/components/SavedItemPane.jsx | 1 + src/components/app.jsx | 70 ++++++++++++++++++++++++-------- src/db.js | 5 +++ src/itemService.js | 5 +++ 4 files changed, 65 insertions(+), 16 deletions(-) diff --git a/src/components/SavedItemPane.jsx b/src/components/SavedItemPane.jsx index 3e606c7c..54991a4c 100644 --- a/src/components/SavedItemPane.jsx +++ b/src/components/SavedItemPane.jsx @@ -13,6 +13,7 @@ export default class SavedItemPane extends Component { } componentWillUpdate(nextProps) { + console.log('feng componentWillUpdate'); if (this.props.items !== nextProps.items) { this.items = Object.values(nextProps.items); this.items.sort(function (a, b) { diff --git a/src/components/app.jsx b/src/components/app.jsx index 97abe97f..2693b8cd 100644 --- a/src/components/app.jsx +++ b/src/components/app.jsx @@ -137,6 +137,7 @@ export default class App extends Component { // window.zd_libraryBtHander = this.openAddLibrary.bind(this) } firebase.auth().onAuthStateChanged(async (user) => { + console.debug('feng onAuthStateChanged'); await this.setState({ isLoginModalOpen: false }); if (user) { log('You are -> ', user); @@ -455,6 +456,19 @@ BookLibService.Borrow(id) { return d.promise; } + alertIfExceedItemsLimit() { + const r = this.checkItemsLimit(); + if (!r) this.alertItemsLimit(); + return r; + } + + alertItemsLimit() { + alert( + `You have ${Object.keys(this.state.user.items).length} diagrams, the limit is ${userService.isBasic() ? 20 : 3}. Upgrade now for more storage.`, + ); + this.proBtnClickHandler(); + } + checkItemsLimit() { if ( !this.state.user || @@ -465,11 +479,17 @@ BookLibService.Borrow(id) { ) { return true; } + return false; + } - alert( - `You have ${Object.keys(this.state.user.items).length} diagrams, the limit is ${userService.isBasic() ? 20 : 3}. Upgrade now for more storage.`, - ); - this.proBtnClickHandler(); + isNewItem(itemId) { + if (!itemId) return true; + const { user } = this.state; + if (user && user.items) { + const found = Object.keys(user.items).some((key) => itemId === key); + return !found; + } + return true; } saveBtnClickHandler() { @@ -482,8 +502,8 @@ BookLibService.Borrow(id) { ? 'saved' : 'new', ); - - if (!this.checkItemsLimit()) { + console.log('feng saveBtnClickHandler'); + if (!this.alertIfExceedItemsLimit()) { mixpanel.track({ event: 'Free Limit', category: '3 diagrams limit', @@ -546,6 +566,7 @@ BookLibService.Borrow(id) { var items = []; if ((window.user || window.zenumlDesktop) && !shouldFetchLocally) { items = await itemService.getAllItems(); + console.log(`feng fetchItems got items:${items.length}`); log('got items'); if (shouldSaveGlobally) { items.forEach((item) => { @@ -585,6 +606,7 @@ BookLibService.Borrow(id) { await this.setState({ isFetchingItems: true, }); + console.log('feng openSavedItemsPane'); this.fetchItems(true).then(async (items) => { await this.setState({ isFetchingItems: false, @@ -624,7 +646,7 @@ BookLibService.Borrow(id) { // Ctrl/⌘ + S if ((event.ctrlKey || event.metaKey) && event.keyCode === 83) { event.preventDefault(); - this.saveItem(); + this.saveItem(true); trackEvent('ui', 'saveItemKeyboardShortcut'); } // Ctrl/⌘ + Shift + 5 @@ -840,7 +862,8 @@ BookLibService.Borrow(id) { } // Save current item to storage - async saveItem() { + async saveItem(isManual = false) { + console.log('feng saveItem'); if ( !window.user && !window.localStorage[LocalStorageKeys.LOGIN_AND_SAVE_MESSAGE_SEEN] && @@ -858,13 +881,25 @@ BookLibService.Borrow(id) { } trackEvent('ui', LocalStorageKeys.LOGIN_AND_SAVE_MESSAGE_SEEN, 'local'); } - var isNewItem = !this.state.currentItem.id; - if (isNewItem && this.checkItemsLimit()) { - this.proBtnClickHandler(); + var isNewItem = this.isNewItem(this.state.currentItem.id); + let check = this.checkItemsLimit(); + console.log( + `feng saveItem checkItemsLimit:${check} isNewItem:${isNewItem}`, + ); + if (isNewItem && !check) { + if (isManual) this.alertItemsLimit(); return; } + console.log( + 'feng saveItem before currentItem.id', + this.state.currentItem.id, + ); this.state.currentItem.id = this.state.currentItem.id || 'item-' + generateRandomId(); + console.log( + 'feng saveItem after currentItem.id', + this.state.currentItem.id, + ); await this.setState({ isSaving: true, }); @@ -1062,7 +1097,8 @@ BookLibService.Borrow(id) { } async itemForkBtnClickHandler(item) { - if (!this.checkItemsLimit()) { + console.log('feng itemForkBtnClickHandler'); + if (!this.alertIfExceedItemsLimit()) { mixpanel.track({ event: 'Free Limit', category: '3 diagrams limit', @@ -1079,8 +1115,8 @@ BookLibService.Borrow(id) { async newBtnClickHandler() { mixpanel.track({ event: 'newBtnClick', category: 'ui' }); - - if (!this.checkItemsLimit()) { + console.log('feng newBtnClickHandler'); + if (!this.alertIfExceedItemsLimit()) { mixpanel.track({ event: 'Free Limit', category: '3 diagrams limit', @@ -1208,7 +1244,8 @@ BookLibService.Borrow(id) { } exportBtnClickHandler(e) { - if (!this.checkItemsLimit()) { + console.log('feng exportBtnClickHandler'); + if (!this.alertIfExceedItemsLimit()) { mixpanel.track({ event: 'Free Limit', category: '3 diagrams limit', @@ -1304,7 +1341,8 @@ BookLibService.Borrow(id) { * Called from inside ask-to-import-modal */ importCreationsAndSettingsIntoApp() { - if (!this.checkItemsLimit()) { + console.log('feng importCreationsAndSettingsIntoApp'); + if (!this.alertIfExceedItemsLimit()) { mixpanel.track({ event: 'Free Limit', category: '3 diagrams limit', diff --git a/src/db.js b/src/db.js index 32da1147..d4cc239f 100644 --- a/src/db.js +++ b/src/db.js @@ -123,6 +123,7 @@ import { log } from './utils'; } async function getUser(userId) { + console.log('feng db getUser'); const remoteDb = await getDb(); return remoteDb .doc(`users/${userId}`) @@ -135,7 +136,11 @@ import { log } from './utils'; merge: true, }, ); + //user.items value source const user = doc.data(); + console.log( + `feng db getUser user.items:${Object.keys(user.items).length}`, + ); Object.assign(window.user, user); return user; }); diff --git a/src/itemService.js b/src/itemService.js index e4dd4eb2..f63df5fe 100644 --- a/src/itemService.js +++ b/src/itemService.js @@ -20,6 +20,7 @@ if (window.zenumlDesktop) { async getUserItemIds() { if (window.user) { return new Promise((resolve) => { + console.log('feng getUserItemIds step1'); resolve(window.user.items || {}); }); } @@ -31,6 +32,7 @@ if (window.zenumlDesktop) { if (!doc.exists) { return {}; } + console.log('feng getUserItemIds step2'); return doc.data().items; }); }, @@ -39,6 +41,7 @@ if (window.zenumlDesktop) { var t = Date.now(); var d = deferred(); let itemIds = await this.getUserItemIds(); + console.log(`feng getAllItems itemIds:${Object.keys(itemIds).length}`); itemIds = Object.getOwnPropertyNames(itemIds || {}); log('itemids', itemIds); @@ -156,6 +159,7 @@ if (window.zenumlDesktop) { [`items.${id}`]: true, }); // Set these items on our cached user object too + console.log('feng saveItems'); window.user.items = window.user.items || {}; window.user.items[id] = true; } @@ -209,6 +213,7 @@ if (window.zenumlDesktop) { }) .then((arg) => { log(`Item ${itemId} set for user`, arg); + console.log('feng set user.items'); window.user.items = window.user.items || {}; window.user.items[itemId] = true; }) From 4a1ba99b07acb46eb53b0adefb6c33edbfd9e6de Mon Sep 17 00:00:00 2001 From: danshuitaihejie <474182370@qq.com> Date: Sat, 29 Jun 2024 16:02:46 +0800 Subject: [PATCH 03/18] refactor: add IPlan and plan instance --- src/services/planService.js | 45 +++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/services/planService.js b/src/services/planService.js index 6a0859b9..d953c4bd 100644 --- a/src/services/planService.js +++ b/src/services/planService.js @@ -15,6 +15,51 @@ const getProductByPlanType = (planType) => { return product; }; +// 定义接口 +const IPlan = { + getMaxItemsCount: () => {}, + canCustomizeCSS: () => {}, + getProductId: () => {}, + getPlanType: () => {}, +}; + +const getPlanByType = (planType) => { + const planMap = { + free: { + getMaxItemsCount: () => 3, + canCustomizeCSS: () => false, + getProductId: () => '', + getPlanType: () => 'free', + }, + 'basic-monthly': { + getMaxItemsCount: () => 20, + canCustomizeCSS: () => false, + getProductId: () => config.paddleProductBasicMonthly, + getPlanType: () => 'basic-monthly', + }, + 'plus-monthly': { + getMaxItemsCount: () => 999999, + canCustomizeCSS: () => true, + getProductId: () => config.paddleProductPlusMonthly, + getPlanType: () => 'plus-monthly', + }, + 'basic-yearly': { + getMaxItemsCount: () => 20, + canCustomizeCSS: () => false, + getProductId: () => config.paddleProductBasicYearly, + getPlanType: () => 'basic-yearly', + }, + 'plus-yearly': { + getMaxItemsCount: () => 999999, + canCustomizeCSS: () => true, + getProductId: () => config.paddleProductPlusYearly, + getPlanType: () => 'plus-yearly', + }, + }; + + return planMap[planType] || planMap['free']; +}; export default { getProductByPlanType: getProductByPlanType, + getPlanByType: getPlanByType, }; From e1203fd072774aabd0ebe2d6aaec39ec08df89aa Mon Sep 17 00:00:00 2001 From: danshuitaihejie <474182370@qq.com> Date: Sat, 29 Jun 2024 16:05:10 +0800 Subject: [PATCH 04/18] refactor: using IPlan to check items limit --- src/components/app.jsx | 20 +++++++++----------- src/services/user_service.js | 4 ++++ 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/components/app.jsx b/src/components/app.jsx index 2693b8cd..aafc7b52 100644 --- a/src/components/app.jsx +++ b/src/components/app.jsx @@ -463,23 +463,21 @@ BookLibService.Borrow(id) { } alertItemsLimit() { + var plan = userService.getPlan(); alert( - `You have ${Object.keys(this.state.user.items).length} diagrams, the limit is ${userService.isBasic() ? 20 : 3}. Upgrade now for more storage.`, + `You have ${this.getUserItemsCount()} diagrams, the limit is ${plan.getMaxItemsCount()}. Upgrade now for more storage.`, ); this.proBtnClickHandler(); } + getUserItemsCount() { + if (!this.state.user || !this.state.user.items) return 0; + return Object.keys(this.state.user.items).length; + } + checkItemsLimit() { - if ( - !this.state.user || - !this.state.user.items || - Object.keys(this.state.user.items).length <= 3 || - userService.isPlusOrAdvanced() || - (Object.keys(this.state.user.items).length <= 20 && userService.isBasic()) - ) { - return true; - } - return false; + var currentItemsCount = this.getUserItemsCount(); + return userService.getPlan().getMaxItemsCount() >= currentItemsCount; } isNewItem(itemId) { diff --git a/src/services/user_service.js b/src/services/user_service.js index 2f0fe4b8..cafba66c 100644 --- a/src/services/user_service.js +++ b/src/services/user_service.js @@ -1,3 +1,4 @@ +import planService from './planService'; const user = () => window.user; const subscription = () => user() && user().subscription; @@ -26,6 +27,9 @@ export default { const currentSubscription = subscription(); return getPlanTypeFromPassthrough(currentSubscription.passthrough); }, + getPlan: function () { + return planService.getPlanByType(this.getPlanType()); + }, }; // Compatible with previous pro users, before subscription.passthrough only stored userId From a87a950ec6d762b4026c0237de6c6cf31e09478b Mon Sep 17 00:00:00 2001 From: danshuitaihejie <474182370@qq.com> Date: Sat, 29 Jun 2024 16:06:12 +0800 Subject: [PATCH 05/18] refactor: using IPlan to check if can edit css --- src/components/ContentWrap.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ContentWrap.jsx b/src/components/ContentWrap.jsx index 63902aa1..33f95c0c 100644 --- a/src/components/ContentWrap.jsx +++ b/src/components/ContentWrap.jsx @@ -804,7 +804,7 @@ export default class ContentWrap extends Component { onCSSActiviation() { if (!window.user) { this.props.onLogin(); - } else if (userService.isPlusOrAdvanced()) { + } else if (userService.getPlan().canCustomizeCSS()) { return true; } else { this.props.onProFeature(); From 4544cfac019ae159d9f035dcefde6db85ee7b4bb Mon Sep 17 00:00:00 2001 From: danshuitaihejie <474182370@qq.com> Date: Sat, 29 Jun 2024 16:21:51 +0800 Subject: [PATCH 06/18] refactor: remove useless function in user_service --- src/services/user_service.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/services/user_service.js b/src/services/user_service.js index cafba66c..01128ca2 100644 --- a/src/services/user_service.js +++ b/src/services/user_service.js @@ -13,15 +13,6 @@ export default { subscription().status === 'trialing') ); }, - isBasic: function () { - return this.getPlanType().includes('basic'); - }, - isPlus: function () { - return this.getPlanType().includes('plus'); - }, - isPlusOrAdvanced: function () { - return this.isPlus(); - }, getPlanType: function () { if (!this.isSubscribed()) return 'free'; const currentSubscription = subscription(); From 4ea340297998106ad4c3225d4a031b40761d141d Mon Sep 17 00:00:00 2001 From: danshuitaihejie <474182370@qq.com> Date: Sat, 29 Jun 2024 16:27:32 +0800 Subject: [PATCH 07/18] refactor: replace getProductByPlanType with IPlan --- src/components/subscription/UpgradeLink.jsx | 3 +-- src/services/planService.js | 15 --------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/src/components/subscription/UpgradeLink.jsx b/src/components/subscription/UpgradeLink.jsx index dc23be53..b8524a44 100644 --- a/src/components/subscription/UpgradeLink.jsx +++ b/src/components/subscription/UpgradeLink.jsx @@ -1,5 +1,4 @@ import planService from '../../services/planService'; -import userService from '../../services/user_service'; const UpgradeLink = (props) => { const checkout = (e) => { @@ -7,7 +6,7 @@ const UpgradeLink = (props) => { props.preActionCallback(); Paddle.Checkout.open({ - product: planService.getProductByPlanType(props.planType), + product: planService.getPlanByType(props.planType).getProductId(), email: props.userEmail, passthrough: JSON.stringify({ userId: props.userId, diff --git a/src/services/planService.js b/src/services/planService.js index d953c4bd..9db3eae4 100644 --- a/src/services/planService.js +++ b/src/services/planService.js @@ -2,20 +2,6 @@ import config from './configuration'; //TODO(refactor): It is necessary to integrate more plan-related logic into this module. -const getProductByPlanType = (planType) => { - const productMap = { - 'basic-monthly': config.paddleProductBasicMonthly, - 'plus-monthly': config.paddleProductPlusMonthly, - 'basic-yearly': config.paddleProductBasicYearly, - 'plus-yearly': config.paddleProductPlusYearly, - }; - - const product = productMap[planType] || ''; - console.debug('getProductByPlanType', planType, product); - return product; -}; - -// 定义接口 const IPlan = { getMaxItemsCount: () => {}, canCustomizeCSS: () => {}, @@ -60,6 +46,5 @@ const getPlanByType = (planType) => { return planMap[planType] || planMap['free']; }; export default { - getProductByPlanType: getProductByPlanType, getPlanByType: getPlanByType, }; From cd96746b2891f7134b0fda3a80bf54fff16fe444 Mon Sep 17 00:00:00 2001 From: danshuitaihejie <474182370@qq.com> Date: Sat, 29 Jun 2024 16:45:04 +0800 Subject: [PATCH 08/18] refactor: move 'checkPlanTypeFromUserSubscription' from user service to plan service --- src/services/planService.js | 23 +++++++++++++++++++++++ src/services/user_service.js | 24 ++++-------------------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/services/planService.js b/src/services/planService.js index 9db3eae4..6fd9e6bb 100644 --- a/src/services/planService.js +++ b/src/services/planService.js @@ -45,6 +45,29 @@ const getPlanByType = (planType) => { return planMap[planType] || planMap['free']; }; + +const checkPlanTypeFromUserSubscription = ( + isSubscribed, + getSubscriptionPassthroughFunc, +) => { + if (!isSubscribed) return 'free'; + // Compatible with previous pro users, before subscription.passthrough only stored userId + const passthrough = getSubscriptionPassthroughFunc(); + return isJSONString(passthrough) + ? JSON.parse(passthrough).planType + : 'basic-monthly'; +}; + +function isJSONString(str) { + try { + JSON.parse(str); + return true; + } catch (e) { + return false; + } +} + export default { + checkPlanTypeFromUserSubscription: checkPlanTypeFromUserSubscription, getPlanByType: getPlanByType, }; diff --git a/src/services/user_service.js b/src/services/user_service.js index 01128ca2..c427b5bc 100644 --- a/src/services/user_service.js +++ b/src/services/user_service.js @@ -6,7 +6,6 @@ export default { user: user, subscription: subscription, isSubscribed: function () { - //console.debug('subscription', subscription()); return ( subscription() && (subscription().status === 'active' || @@ -14,27 +13,12 @@ export default { ); }, getPlanType: function () { - if (!this.isSubscribed()) return 'free'; - const currentSubscription = subscription(); - return getPlanTypeFromPassthrough(currentSubscription.passthrough); + return planService.checkPlanTypeFromUserSubscription( + this.isSubscribed(), + () => subscription().passthrough, + ); }, getPlan: function () { return planService.getPlanByType(this.getPlanType()); }, }; - -// Compatible with previous pro users, before subscription.passthrough only stored userId -function getPlanTypeFromPassthrough(passthrough) { - return isJSONString(passthrough) - ? JSON.parse(passthrough).planType - : 'basic-monthly'; -} - -function isJSONString(str) { - try { - JSON.parse(str); - return true; - } catch (e) { - return false; - } -} From d616102c1cc909222d24df8c719e38cddf07186b Mon Sep 17 00:00:00 2001 From: danshuitaihejie <474182370@qq.com> Date: Sat, 29 Jun 2024 17:40:29 +0800 Subject: [PATCH 09/18] refactor: improve naming and adjust corresponding decision logic for 'alertAndTrackIfExceedItemsLimit' --- src/components/app.jsx | 67 ++++++++++++------------------------------ 1 file changed, 19 insertions(+), 48 deletions(-) diff --git a/src/components/app.jsx b/src/components/app.jsx index aafc7b52..953fbbae 100644 --- a/src/components/app.jsx +++ b/src/components/app.jsx @@ -456,16 +456,23 @@ BookLibService.Borrow(id) { return d.promise; } - alertIfExceedItemsLimit() { - const r = this.checkItemsLimit(); - if (!r) this.alertItemsLimit(); - return r; + alertAndTrackIfExceedItemsLimit(userActionName) { + const exceed = !this.checkItemsLimit(); + if (exceed) { + this.alertItemsLimit(); + var plan = userService.getPlan(); + mixpanel.track({ + event: `${plan.getPlanType()} Limit`, + category: `${plan.getMaxItemsCount()} diagrams limit`, + label: userActionName, + }); + } + return exceed; } alertItemsLimit() { - var plan = userService.getPlan(); alert( - `You have ${this.getUserItemsCount()} diagrams, the limit is ${plan.getMaxItemsCount()}. Upgrade now for more storage.`, + `You have ${this.getUserItemsCount()} diagrams, the limit is ${userService.getPlan().getMaxItemsCount()}. Upgrade now for more storage.`, ); this.proBtnClickHandler(); } @@ -476,8 +483,7 @@ BookLibService.Borrow(id) { } checkItemsLimit() { - var currentItemsCount = this.getUserItemsCount(); - return userService.getPlan().getMaxItemsCount() >= currentItemsCount; + return userService.getPlan().getMaxItemsCount() >= this.getUserItemsCount(); } isNewItem(itemId) { @@ -501,14 +507,7 @@ BookLibService.Borrow(id) { : 'new', ); console.log('feng saveBtnClickHandler'); - if (!this.alertIfExceedItemsLimit()) { - mixpanel.track({ - event: 'Free Limit', - category: '3 diagrams limit', - label: 'Save', - }); - return; - } + if (this.alertAndTrackIfExceedItemsLimit('Save')) return; if (this.state.user || window.zenumlDesktop) { this.saveItem(); @@ -1096,14 +1095,7 @@ BookLibService.Borrow(id) { async itemForkBtnClickHandler(item) { console.log('feng itemForkBtnClickHandler'); - if (!this.alertIfExceedItemsLimit()) { - mixpanel.track({ - event: 'Free Limit', - category: '3 diagrams limit', - label: 'Fork', - }); - return; - } + if (this.alertAndTrackIfExceedItemsLimit('Fork')) return; await this.toggleSavedItemsPane(); setTimeout(() => { @@ -1114,14 +1106,7 @@ BookLibService.Borrow(id) { async newBtnClickHandler() { mixpanel.track({ event: 'newBtnClick', category: 'ui' }); console.log('feng newBtnClickHandler'); - if (!this.alertIfExceedItemsLimit()) { - mixpanel.track({ - event: 'Free Limit', - category: '3 diagrams limit', - label: 'New', - }); - return; - } + if (this.alertAndTrackIfExceedItemsLimit('New')) return; if (this.state.unsavedEditCount) { var shouldDiscard = confirm( @@ -1243,14 +1228,7 @@ BookLibService.Borrow(id) { exportBtnClickHandler(e) { console.log('feng exportBtnClickHandler'); - if (!this.alertIfExceedItemsLimit()) { - mixpanel.track({ - event: 'Free Limit', - category: '3 diagrams limit', - label: 'Fork', - }); - return; - } + if (this.alertAndTrackIfExceedItemsLimit('Export')) return; this.exportItems(); e.preventDefault(); @@ -1340,14 +1318,7 @@ BookLibService.Borrow(id) { */ importCreationsAndSettingsIntoApp() { console.log('feng importCreationsAndSettingsIntoApp'); - if (!this.alertIfExceedItemsLimit()) { - mixpanel.track({ - event: 'Free Limit', - category: '3 diagrams limit', - label: 'ImportCreations', - }); - return; - } + if (this.alertAndTrackIfExceedItemsLimit('Import')) return; this.mergeImportedItems(this.oldSavedItems).then(() => { trackEvent('fn', 'oldItemsImported'); this.dontAskToImportAnymore(); From 9345847506d6be51edd1348ae3605bd191af5c2c Mon Sep 17 00:00:00 2001 From: danshuitaihejie <474182370@qq.com> Date: Sat, 29 Jun 2024 18:27:19 +0800 Subject: [PATCH 10/18] refactor: remove unnecessary debugging logs --- src/components/SavedItemPane.jsx | 1 - src/components/app.jsx | 21 +-------------------- src/db.js | 4 ---- src/itemService.js | 5 ----- 4 files changed, 1 insertion(+), 30 deletions(-) diff --git a/src/components/SavedItemPane.jsx b/src/components/SavedItemPane.jsx index 54991a4c..3e606c7c 100644 --- a/src/components/SavedItemPane.jsx +++ b/src/components/SavedItemPane.jsx @@ -13,7 +13,6 @@ export default class SavedItemPane extends Component { } componentWillUpdate(nextProps) { - console.log('feng componentWillUpdate'); if (this.props.items !== nextProps.items) { this.items = Object.values(nextProps.items); this.items.sort(function (a, b) { diff --git a/src/components/app.jsx b/src/components/app.jsx index 953fbbae..0241a864 100644 --- a/src/components/app.jsx +++ b/src/components/app.jsx @@ -137,7 +137,6 @@ export default class App extends Component { // window.zd_libraryBtHander = this.openAddLibrary.bind(this) } firebase.auth().onAuthStateChanged(async (user) => { - console.debug('feng onAuthStateChanged'); await this.setState({ isLoginModalOpen: false }); if (user) { log('You are -> ', user); @@ -506,7 +505,6 @@ BookLibService.Borrow(id) { ? 'saved' : 'new', ); - console.log('feng saveBtnClickHandler'); if (this.alertAndTrackIfExceedItemsLimit('Save')) return; if (this.state.user || window.zenumlDesktop) { @@ -563,7 +561,6 @@ BookLibService.Borrow(id) { var items = []; if ((window.user || window.zenumlDesktop) && !shouldFetchLocally) { items = await itemService.getAllItems(); - console.log(`feng fetchItems got items:${items.length}`); log('got items'); if (shouldSaveGlobally) { items.forEach((item) => { @@ -603,7 +600,6 @@ BookLibService.Borrow(id) { await this.setState({ isFetchingItems: true, }); - console.log('feng openSavedItemsPane'); this.fetchItems(true).then(async (items) => { await this.setState({ isFetchingItems: false, @@ -860,7 +856,6 @@ BookLibService.Borrow(id) { // Save current item to storage async saveItem(isManual = false) { - console.log('feng saveItem'); if ( !window.user && !window.localStorage[LocalStorageKeys.LOGIN_AND_SAVE_MESSAGE_SEEN] && @@ -880,23 +875,13 @@ BookLibService.Borrow(id) { } var isNewItem = this.isNewItem(this.state.currentItem.id); let check = this.checkItemsLimit(); - console.log( - `feng saveItem checkItemsLimit:${check} isNewItem:${isNewItem}`, - ); + console.log(`saveItem checkItemsLimit:${check} isNewItem:${isNewItem}`); if (isNewItem && !check) { if (isManual) this.alertItemsLimit(); return; } - console.log( - 'feng saveItem before currentItem.id', - this.state.currentItem.id, - ); this.state.currentItem.id = this.state.currentItem.id || 'item-' + generateRandomId(); - console.log( - 'feng saveItem after currentItem.id', - this.state.currentItem.id, - ); await this.setState({ isSaving: true, }); @@ -1094,7 +1079,6 @@ BookLibService.Borrow(id) { } async itemForkBtnClickHandler(item) { - console.log('feng itemForkBtnClickHandler'); if (this.alertAndTrackIfExceedItemsLimit('Fork')) return; await this.toggleSavedItemsPane(); @@ -1105,7 +1089,6 @@ BookLibService.Borrow(id) { async newBtnClickHandler() { mixpanel.track({ event: 'newBtnClick', category: 'ui' }); - console.log('feng newBtnClickHandler'); if (this.alertAndTrackIfExceedItemsLimit('New')) return; if (this.state.unsavedEditCount) { @@ -1227,7 +1210,6 @@ BookLibService.Borrow(id) { } exportBtnClickHandler(e) { - console.log('feng exportBtnClickHandler'); if (this.alertAndTrackIfExceedItemsLimit('Export')) return; this.exportItems(); @@ -1317,7 +1299,6 @@ BookLibService.Borrow(id) { * Called from inside ask-to-import-modal */ importCreationsAndSettingsIntoApp() { - console.log('feng importCreationsAndSettingsIntoApp'); if (this.alertAndTrackIfExceedItemsLimit('Import')) return; this.mergeImportedItems(this.oldSavedItems).then(() => { trackEvent('fn', 'oldItemsImported'); diff --git a/src/db.js b/src/db.js index d4cc239f..9482a50f 100644 --- a/src/db.js +++ b/src/db.js @@ -123,7 +123,6 @@ import { log } from './utils'; } async function getUser(userId) { - console.log('feng db getUser'); const remoteDb = await getDb(); return remoteDb .doc(`users/${userId}`) @@ -138,9 +137,6 @@ import { log } from './utils'; ); //user.items value source const user = doc.data(); - console.log( - `feng db getUser user.items:${Object.keys(user.items).length}`, - ); Object.assign(window.user, user); return user; }); diff --git a/src/itemService.js b/src/itemService.js index f63df5fe..e4dd4eb2 100644 --- a/src/itemService.js +++ b/src/itemService.js @@ -20,7 +20,6 @@ if (window.zenumlDesktop) { async getUserItemIds() { if (window.user) { return new Promise((resolve) => { - console.log('feng getUserItemIds step1'); resolve(window.user.items || {}); }); } @@ -32,7 +31,6 @@ if (window.zenumlDesktop) { if (!doc.exists) { return {}; } - console.log('feng getUserItemIds step2'); return doc.data().items; }); }, @@ -41,7 +39,6 @@ if (window.zenumlDesktop) { var t = Date.now(); var d = deferred(); let itemIds = await this.getUserItemIds(); - console.log(`feng getAllItems itemIds:${Object.keys(itemIds).length}`); itemIds = Object.getOwnPropertyNames(itemIds || {}); log('itemids', itemIds); @@ -159,7 +156,6 @@ if (window.zenumlDesktop) { [`items.${id}`]: true, }); // Set these items on our cached user object too - console.log('feng saveItems'); window.user.items = window.user.items || {}; window.user.items[id] = true; } @@ -213,7 +209,6 @@ if (window.zenumlDesktop) { }) .then((arg) => { log(`Item ${itemId} set for user`, arg); - console.log('feng set user.items'); window.user.items = window.user.items || {}; window.user.items[itemId] = true; }) From 273330736b7b975aa3d90701106df3bc98f0d4cf Mon Sep 17 00:00:00 2001 From: danshuitaihejie <474182370@qq.com> Date: Sat, 29 Jun 2024 18:28:41 +0800 Subject: [PATCH 11/18] refactor: add saveItem debug log --- src/components/app.jsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/app.jsx b/src/components/app.jsx index 0241a864..6107abab 100644 --- a/src/components/app.jsx +++ b/src/components/app.jsx @@ -874,9 +874,12 @@ BookLibService.Borrow(id) { trackEvent('ui', LocalStorageKeys.LOGIN_AND_SAVE_MESSAGE_SEEN, 'local'); } var isNewItem = this.isNewItem(this.state.currentItem.id); - let check = this.checkItemsLimit(); - console.log(`saveItem checkItemsLimit:${check} isNewItem:${isNewItem}`); - if (isNewItem && !check) { + const check = this.checkItemsLimit(); + var preventedSaving = isNewItem && !check; + console.debug( + `saveItem preventedSaving:${preventedSaving} user:${window.user} isManual:${isManual} checkItemsLimit:${check} isNewItem:${isNewItem}`, + ); + if (preventedSaving) { if (isManual) this.alertItemsLimit(); return; } From 1c66ce3c1edd64390d27f700a0e406a3718e46e0 Mon Sep 17 00:00:00 2001 From: danshuitaihejie <474182370@qq.com> Date: Tue, 2 Jul 2024 04:50:36 +0800 Subject: [PATCH 12/18] refactor: check isSubscribed by plan --- src/components/MainHeader.jsx | 2 +- src/components/subscription/SubscriptionAction.jsx | 5 +++-- src/services/planService.js | 5 +++++ src/services/user_service.js | 11 +++-------- .../ProductVersionLabel/ProductVersionLabel.js | 2 +- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/components/MainHeader.jsx b/src/components/MainHeader.jsx index 0c187b69..75e3c20b 100644 --- a/src/components/MainHeader.jsx +++ b/src/components/MainHeader.jsx @@ -57,7 +57,7 @@ export function MainHeader(props) { mixpanel.track({ event: 'toLanguageGuide', category: 'ui' }); }; - const isSubscribed = userService.isSubscribed(); + const isSubscribed = userService.getPlan().isSubscribed(); return (
diff --git a/src/components/subscription/SubscriptionAction.jsx b/src/components/subscription/SubscriptionAction.jsx index 2034b323..03fd4dca 100644 --- a/src/components/subscription/SubscriptionAction.jsx +++ b/src/components/subscription/SubscriptionAction.jsx @@ -10,9 +10,10 @@ const SubscriptionAction = (props) => { return null; } - if (userService.isSubscribed()) { + const plan = userService.getPlan(); + if (plan.isSubscribed()) { const subscription = userService.subscription(); - if (props.planType == userService.getPlanType()) { + if (props.planType == plan.getPlanType()) { return ; } return ; diff --git a/src/services/planService.js b/src/services/planService.js index 6fd9e6bb..7bff93e8 100644 --- a/src/services/planService.js +++ b/src/services/planService.js @@ -16,30 +16,35 @@ const getPlanByType = (planType) => { canCustomizeCSS: () => false, getProductId: () => '', getPlanType: () => 'free', + isSubscribed: () => false, }, 'basic-monthly': { getMaxItemsCount: () => 20, canCustomizeCSS: () => false, getProductId: () => config.paddleProductBasicMonthly, getPlanType: () => 'basic-monthly', + isSubscribed: () => true, }, 'plus-monthly': { getMaxItemsCount: () => 999999, canCustomizeCSS: () => true, getProductId: () => config.paddleProductPlusMonthly, getPlanType: () => 'plus-monthly', + isSubscribed: () => true, }, 'basic-yearly': { getMaxItemsCount: () => 20, canCustomizeCSS: () => false, getProductId: () => config.paddleProductBasicYearly, getPlanType: () => 'basic-yearly', + isSubscribed: () => true, }, 'plus-yearly': { getMaxItemsCount: () => 999999, canCustomizeCSS: () => true, getProductId: () => config.paddleProductPlusYearly, getPlanType: () => 'plus-yearly', + isSubscribed: () => true, }, }; diff --git a/src/services/user_service.js b/src/services/user_service.js index c427b5bc..2a69137b 100644 --- a/src/services/user_service.js +++ b/src/services/user_service.js @@ -5,16 +5,11 @@ const subscription = () => user() && user().subscription; export default { user: user, subscription: subscription, - isSubscribed: function () { - return ( - subscription() && - (subscription().status === 'active' || - subscription().status === 'trialing') - ); - }, getPlanType: function () { + const status = subscription()?.status; + const isSubscribed = status === 'active' || status === 'trialing'; return planService.checkPlanTypeFromUserSubscription( - this.isSubscribed(), + isSubscribed, () => subscription().passthrough, ); }, diff --git a/src/zenuml/components/MainHeader/ProductVersionLabel/ProductVersionLabel.js b/src/zenuml/components/MainHeader/ProductVersionLabel/ProductVersionLabel.js index acf10ead..43298ec4 100644 --- a/src/zenuml/components/MainHeader/ProductVersionLabel/ProductVersionLabel.js +++ b/src/zenuml/components/MainHeader/ProductVersionLabel/ProductVersionLabel.js @@ -7,7 +7,7 @@ export function ProductVersionLabel(props) { ? 'Please login to upgrade to Pro' : 'Get more out of ZenUML — Go Pro'; - if (!window.user || userService.isSubscribed()) return null; + if (!window.user || userService.getPlan().isSubscribed()) return null; return ( Date: Tue, 2 Jul 2024 05:33:57 +0800 Subject: [PATCH 13/18] refactor: remove isJSONString --- src/services/planService.js | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/services/planService.js b/src/services/planService.js index 7bff93e8..83474a83 100644 --- a/src/services/planService.js +++ b/src/services/planService.js @@ -56,21 +56,15 @@ const checkPlanTypeFromUserSubscription = ( getSubscriptionPassthroughFunc, ) => { if (!isSubscribed) return 'free'; - // Compatible with previous pro users, before subscription.passthrough only stored userId - const passthrough = getSubscriptionPassthroughFunc(); - return isJSONString(passthrough) - ? JSON.parse(passthrough).planType - : 'basic-monthly'; -}; -function isJSONString(str) { try { - JSON.parse(str); - return true; - } catch (e) { - return false; + return ( + JSON.parse(getSubscriptionPassthroughFunc())?.planType || 'basic-monthly' + ); + } catch { + return 'basic-monthly'; } -} +}; export default { checkPlanTypeFromUserSubscription: checkPlanTypeFromUserSubscription, From a80a8126ee627a41149c49e925632ad9898c2b38 Mon Sep 17 00:00:00 2001 From: danshuitaihejie <474182370@qq.com> Date: Tue, 2 Jul 2024 05:37:22 +0800 Subject: [PATCH 14/18] fix!: old pro users are equal to new plus-monthly --- src/services/planService.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/planService.js b/src/services/planService.js index 83474a83..a70c0077 100644 --- a/src/services/planService.js +++ b/src/services/planService.js @@ -59,10 +59,10 @@ const checkPlanTypeFromUserSubscription = ( try { return ( - JSON.parse(getSubscriptionPassthroughFunc())?.planType || 'basic-monthly' + JSON.parse(getSubscriptionPassthroughFunc())?.planType || 'plus-monthly' ); } catch { - return 'basic-monthly'; + return 'plus-monthly'; } }; From 525bdef72e90feed14f45d396d6bf09f7e093403 Mon Sep 17 00:00:00 2001 From: danshuitaihejie <474182370@qq.com> Date: Tue, 2 Jul 2024 05:39:08 +0800 Subject: [PATCH 15/18] refactor: remove useless IPlan --- src/services/planService.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/services/planService.js b/src/services/planService.js index a70c0077..28950c9b 100644 --- a/src/services/planService.js +++ b/src/services/planService.js @@ -1,14 +1,5 @@ import config from './configuration'; -//TODO(refactor): It is necessary to integrate more plan-related logic into this module. - -const IPlan = { - getMaxItemsCount: () => {}, - canCustomizeCSS: () => {}, - getProductId: () => {}, - getPlanType: () => {}, -}; - const getPlanByType = (planType) => { const planMap = { free: { From a85ae0d7b6c90d8f434b9546f47184b4bf2252f7 Mon Sep 17 00:00:00 2001 From: danshuitaihejie <474182370@qq.com> Date: Sat, 6 Jul 2024 14:17:27 +0800 Subject: [PATCH 16/18] fix: using this.state.savedItems to check user items --- src/components/app.jsx | 72 +++++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 26 deletions(-) diff --git a/src/components/app.jsx b/src/components/app.jsx index 6107abab..fa1cd29e 100644 --- a/src/components/app.jsx +++ b/src/components/app.jsx @@ -143,30 +143,34 @@ export default class App extends Component { alertsService.add('You are now logged in!'); await this.setState({ user }); window.user = user; - if (!window.localStorage[LocalStorageKeys.ASKED_TO_IMPORT_CREATIONS]) { - this.fetchItems(false, true).then(async (items) => { - if (!items.length) { - return; - } - this.oldSavedItems = items; - this.oldSavedCreationsCount = items.length; - await this.setState({ - isAskToImportModalOpen: true, - }); - mixpanel.track({ event: 'askToImportModalSeen', category: 'ui' }); - }); - } window.db.getUser(user.uid).then(async (customUser) => { if (customUser) { const prefs = { ...this.state.prefs }; Object.assign(prefs, user.settings); await this.setState({ prefs: prefs }); await this.updateSetting(); + await this.fetchSavedItems(); } if (this.onUserItemsResolved) { this.onUserItemsResolved(user.items); } + + if ( + !window.localStorage[LocalStorageKeys.ASKED_TO_IMPORT_CREATIONS] + ) { + this.fetchItems(false, true).then(async (items) => { + if (!items.length) { + return; + } + this.oldSavedItems = items; + this.oldSavedCreationsCount = items.length; + await this.setState({ + isAskToImportModalOpen: true, + }); + mixpanel.track({ event: 'askToImportModalSeen', category: 'ui' }); + }); + } }); //load subscription from firestore @@ -455,8 +459,8 @@ BookLibService.Borrow(id) { return d.promise; } - alertAndTrackIfExceedItemsLimit(userActionName) { - const exceed = !this.checkItemsLimit(); + alertAndTrackIfExceedItemsLimit(userActionName, isNewItem = false) { + const exceed = !this.checkItemsLimit(isNewItem); if (exceed) { this.alertItemsLimit(); var plan = userService.getPlan(); @@ -477,19 +481,27 @@ BookLibService.Borrow(id) { } getUserItemsCount() { - if (!this.state.user || !this.state.user.items) return 0; - return Object.keys(this.state.user.items).length; + return Object.keys(this.getUserItems()).length; + } + + getUserItems() { + if (!this.state.savedItems) return []; + return this.state.savedItems; } - checkItemsLimit() { - return userService.getPlan().getMaxItemsCount() >= this.getUserItemsCount(); + checkItemsLimit(isNewItem = false) { + let count = this.getUserItemsCount(); + if (isNewItem) count++; + return userService.getPlan().getMaxItemsCount() >= count; } isNewItem(itemId) { if (!itemId) return true; const { user } = this.state; if (user && user.items) { - const found = Object.keys(user.items).some((key) => itemId === key); + const found = Object.keys(this.getUserItems()).some( + (key) => itemId === key, + ); return !found; } return true; @@ -525,7 +537,6 @@ BookLibService.Borrow(id) { await this.setState({ savedItems: { ...this.state.savedItems }, }); - await this.toggleSavedItemsPane(); // HACK: Set overflow after sometime so that the items can animate without getting cropped. // setTimeout(() => $('#js-saved-items-wrap').style.overflowY = 'auto', 1000); @@ -597,6 +608,12 @@ BookLibService.Borrow(id) { } async openSavedItemsPane() { + await this.fetchSavedItems(async (items) => { + await this.populateItemsInSavedPane(items); + }); + } + + async fetchSavedItems(callbackFunc) { await this.setState({ isFetchingItems: true, }); @@ -604,7 +621,9 @@ BookLibService.Borrow(id) { await this.setState({ isFetchingItems: false, }); - await this.populateItemsInSavedPane(items); + if (callbackFunc) { + callbackFunc(items); + } }); } @@ -874,7 +893,7 @@ BookLibService.Borrow(id) { trackEvent('ui', LocalStorageKeys.LOGIN_AND_SAVE_MESSAGE_SEEN, 'local'); } var isNewItem = this.isNewItem(this.state.currentItem.id); - const check = this.checkItemsLimit(); + const check = this.checkItemsLimit(isNewItem); var preventedSaving = isNewItem && !check; console.debug( `saveItem preventedSaving:${preventedSaving} user:${window.user} isManual:${isManual} checkItemsLimit:${check} isNewItem:${isNewItem}`, @@ -905,6 +924,7 @@ BookLibService.Borrow(id) { // Push into the items hash if its a new item being saved if (isNewItem) { await itemService.setItemForUser(this.state.currentItem.id); + await this.fetchSavedItems(); } } @@ -1082,7 +1102,7 @@ BookLibService.Borrow(id) { } async itemForkBtnClickHandler(item) { - if (this.alertAndTrackIfExceedItemsLimit('Fork')) return; + if (this.alertAndTrackIfExceedItemsLimit('Fork', true)) return; await this.toggleSavedItemsPane(); setTimeout(() => { @@ -1092,7 +1112,7 @@ BookLibService.Borrow(id) { async newBtnClickHandler() { mixpanel.track({ event: 'newBtnClick', category: 'ui' }); - if (this.alertAndTrackIfExceedItemsLimit('New')) return; + if (this.alertAndTrackIfExceedItemsLimit('New', true)) return; if (this.state.unsavedEditCount) { var shouldDiscard = confirm( @@ -1302,7 +1322,7 @@ BookLibService.Borrow(id) { * Called from inside ask-to-import-modal */ importCreationsAndSettingsIntoApp() { - if (this.alertAndTrackIfExceedItemsLimit('Import')) return; + if (this.alertAndTrackIfExceedItemsLimit('Import', true)) return; this.mergeImportedItems(this.oldSavedItems).then(() => { trackEvent('fn', 'oldItemsImported'); this.dontAskToImportAnymore(); From 874723c0ec70d46b451a791ea5eb32f72ebefffb Mon Sep 17 00:00:00 2001 From: danshuitaihejie <474182370@qq.com> Date: Sat, 6 Jul 2024 15:03:34 +0800 Subject: [PATCH 17/18] fix: import json count limit --- src/components/app.jsx | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/components/app.jsx b/src/components/app.jsx index fa1cd29e..674be7b1 100644 --- a/src/components/app.jsx +++ b/src/components/app.jsx @@ -459,8 +459,8 @@ BookLibService.Borrow(id) { return d.promise; } - alertAndTrackIfExceedItemsLimit(userActionName, isNewItem = false) { - const exceed = !this.checkItemsLimit(isNewItem); + alertAndTrackIfExceedItemsLimit(userActionName, addingItemsCount = 0) { + const exceed = !this.checkItemsLimit(addingItemsCount); if (exceed) { this.alertItemsLimit(); var plan = userService.getPlan(); @@ -489,10 +489,11 @@ BookLibService.Borrow(id) { return this.state.savedItems; } - checkItemsLimit(isNewItem = false) { - let count = this.getUserItemsCount(); - if (isNewItem) count++; - return userService.getPlan().getMaxItemsCount() >= count; + checkItemsLimit(addingItemsCount = 0) { + return ( + userService.getPlan().getMaxItemsCount() >= + this.getUserItemsCount() + addingItemsCount + ); } isNewItem(itemId) { @@ -893,7 +894,7 @@ BookLibService.Borrow(id) { trackEvent('ui', LocalStorageKeys.LOGIN_AND_SAVE_MESSAGE_SEEN, 'local'); } var isNewItem = this.isNewItem(this.state.currentItem.id); - const check = this.checkItemsLimit(isNewItem); + const check = this.checkItemsLimit(isNewItem ? 1 : 0); var preventedSaving = isNewItem && !check; console.debug( `saveItem preventedSaving:${preventedSaving} user:${window.user} isManual:${isManual} checkItemsLimit:${check} isNewItem:${isNewItem}`, @@ -1102,7 +1103,7 @@ BookLibService.Borrow(id) { } async itemForkBtnClickHandler(item) { - if (this.alertAndTrackIfExceedItemsLimit('Fork', true)) return; + if (this.alertAndTrackIfExceedItemsLimit('Fork', 1)) return; await this.toggleSavedItemsPane(); setTimeout(() => { @@ -1112,7 +1113,7 @@ BookLibService.Borrow(id) { async newBtnClickHandler() { mixpanel.track({ event: 'newBtnClick', category: 'ui' }); - if (this.alertAndTrackIfExceedItemsLimit('New', true)) return; + if (this.alertAndTrackIfExceedItemsLimit('New', 1)) return; if (this.state.unsavedEditCount) { var shouldDiscard = confirm( @@ -1270,6 +1271,7 @@ BookLibService.Borrow(id) { } mergeImportedItems(items) { + if (this.alertAndTrackIfExceedItemsLimit('Merge', items.length)) return; var existingItemIds = []; var toMergeItems = {}; const d = deferred(); @@ -1322,7 +1324,10 @@ BookLibService.Borrow(id) { * Called from inside ask-to-import-modal */ importCreationsAndSettingsIntoApp() { - if (this.alertAndTrackIfExceedItemsLimit('Import', true)) return; + if ( + this.alertAndTrackIfExceedItemsLimit('Import', this.oldSavedItems.length) + ) + return; this.mergeImportedItems(this.oldSavedItems).then(() => { trackEvent('fn', 'oldItemsImported'); this.dontAskToImportAnymore(); From 240471d9f9c88e31d886959a1773526410e7803d Mon Sep 17 00:00:00 2001 From: danshuitaihejie <474182370@qq.com> Date: Sat, 6 Jul 2024 15:39:14 +0800 Subject: [PATCH 18/18] fix: blue css participantBackgroundColor error --- src/templates/template-blue.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/templates/template-blue.json b/src/templates/template-blue.json index 33742d77..908611fa 100644 --- a/src/templates/template-blue.json +++ b/src/templates/template-blue.json @@ -6,6 +6,6 @@ "jsMode": "js", "layoutMode": 1, "js": "Client->SGW.\"Get order by id\" {\n svc.Get(id) {\n new X()\n rep.\"load order\" {\n ==\"Start Here\"==\n MF.\"load order from mainframe\"\n ==\"End Here\"==\n if(order == null) {\n @return \n SGW->Client:404\n } else {\n return order\n }\n \n while(true) {\n svc.refresh(data)\n }\n processOrder()\n }\n return order\n }\n return response\n}", - "css": "@fragmentBorderColor: rgba(4, 46, 110, 0.30);\n@nameBackgroundColor: rgba(4, 46, 110, 0.10);\n@labelTextColor: #032C72;\n@messageLineColor: #032C72;\n@occuranceBorderColor: #032C72;\n@occuranceBackgroundColor: #fff;\n@participantLineColor: #032C72;\n@participantBorderColor: #032C72;\n@participantTextColor: #032C72;\n@participantBackgroundColor: rgba(146, 192, 240, 0.30);\n@dividerBackgroundColor: #E28553;\n@dividerBorderColor: #E28553;\n@dividerTextColor: #E28553;\n\n#diagram {\n .sequence-diagram {\n .divider {\n .name {\n padding: 2px 6px 2px 6px;\n border-radius: 4px;\n margin: 0px;\n border-color: @dividerBorderColor;\n color: @dividerTextColor;\n }\n .left {\n background: @dividerBackgroundColor;\n }\n .right {\n background: @dividerBackgroundColor;\n }\n }\n .lifeline {\n .participant {\n font-weight: 400;\n border: 2px solid @participantBorderColor;\n background: @participantBackgroundColor; \n label {\n text-decoration: underline;\n color: @participantTextColor;\n }\n }\n .line {\n border-left-color: @participantLineColor;\n }\n }\n .message {\n .name {\n padding-bottom: 1px;\n color: @messageLineColor;\n }\n border-bottom-color: @messageLineColor;\n svg {\n polyline {\n fill: @messageLineColor;\n stroke: @messageLineColor;\n }\n }\n }\n .message.self {\n svg>polyline:not(.head) {\n fill: none;\n }\n }\n .occurrence {\n background-color: white;\n border: 2px solid @occuranceBorderColor;\n background-color: @occuranceBackgroundColor;\n }\n .fragment {\n margin-top: 8px;\n margin-bottom: 8px;\n border-radius: 4px;\n border: 1px solid @fragmentBorderColor;\n .fragment.par>.block>.statement-container:not(:first-child), .segment:not(:first-child) {\n border-top: 1px solid @fragmentBorderColor;\n }\n .header {\n .name {\n padding: 4px 4px 4px 6px;\n background: @nameBackgroundColor;\n label { /* name label */\n background: transparent;\n padding: 0px;\n color: @labelTextColor;\n }\n }\n }\n }\n .statement-container {\n margin-bottom: 8px;\n }\n }\n}", + "css": "@fragmentBorderColor: rgba(4, 46, 110, 0.30);\n@nameBackgroundColor: rgba(4, 46, 110, 0.10);\n@labelTextColor: #032C72;\n@messageLineColor: #032C72;\n@occuranceBorderColor: #032C72;\n@occuranceBackgroundColor: #fff;\n@participantLineColor: #032C72;\n@participantBorderColor: #032C72;\n@participantTextColor: #032C72;\n@participantBackgroundColor: #dfecfa;\n@dividerBackgroundColor: #E28553;\n@dividerBorderColor: #E28553;\n@dividerTextColor: #E28553;\n\n#diagram {\n .sequence-diagram {\n .divider {\n .name {\n padding: 2px 6px 2px 6px;\n border-radius: 4px;\n margin: 0px;\n border-color: @dividerBorderColor;\n color: @dividerTextColor;\n }\n .left {\n background: @dividerBackgroundColor;\n }\n .right {\n background: @dividerBackgroundColor;\n }\n }\n .lifeline {\n .participant {\n font-weight: 400;\n border: 2px solid @participantBorderColor;\n background: @participantBackgroundColor; \n label {\n text-decoration: underline;\n color: @participantTextColor;\n }\n }\n .line {\n border-left-color: @participantLineColor;\n }\n }\n .message {\n .name {\n padding-bottom: 1px;\n color: @messageLineColor;\n }\n border-bottom-color: @messageLineColor;\n svg {\n polyline {\n fill: @messageLineColor;\n stroke: @messageLineColor;\n }\n }\n }\n .message.self {\n svg>polyline:not(.head) {\n fill: none;\n }\n }\n .occurrence {\n background-color: white;\n border: 2px solid @occuranceBorderColor;\n background-color: @occuranceBackgroundColor;\n }\n .fragment {\n margin-top: 8px;\n margin-bottom: 8px;\n border-radius: 4px;\n border: 1px solid @fragmentBorderColor;\n .fragment.par>.block>.statement-container:not(:first-child), .segment:not(:first-child) {\n border-top: 1px solid @fragmentBorderColor;\n }\n .header {\n .name {\n padding: 4px 4px 4px 6px;\n background: @nameBackgroundColor;\n label { /* name label */\n background: transparent;\n padding: 0px;\n color: @labelTextColor;\n }\n }\n }\n }\n .statement-container {\n margin-bottom: 8px;\n }\n }\n}", "html": "" }