Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/ContentWrap.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion src/components/MainHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export function MainHeader(props) {
mixpanel.track({ event: 'toLanguageGuide', category: 'ui' });
};

const isSubscribed = userService.isSubscribed();
const isSubscribed = userService.getPlan().isSubscribed();

return (
<div className="main-header text-gray-400 py-2 px-8 flex justify-between border-b border-black-700 bg-black-500">
Expand Down
152 changes: 90 additions & 62 deletions src/components/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -455,23 +459,55 @@ BookLibService.Borrow(id) {
return d.promise;
}

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;
alertAndTrackIfExceedItemsLimit(userActionName, addingItemsCount = 0) {
const exceed = !this.checkItemsLimit(addingItemsCount);
if (exceed) {
this.alertItemsLimit();
var plan = userService.getPlan();
mixpanel.track({
event: `${plan.getPlanType()} Limit`,
category: `${plan.getMaxItemsCount()} diagrams limit`,
label: userActionName,
});
}
return exceed;
}

alertItemsLimit() {
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 ${userService.getPlan().getMaxItemsCount()}. Upgrade now for more storage.`,
);
this.proBtnClickHandler();
}

getUserItemsCount() {
return Object.keys(this.getUserItems()).length;
}

getUserItems() {
if (!this.state.savedItems) return [];
return this.state.savedItems;
}

checkItemsLimit(addingItemsCount = 0) {
return (
userService.getPlan().getMaxItemsCount() >=
this.getUserItemsCount() + addingItemsCount
);
}

isNewItem(itemId) {
if (!itemId) return true;
const { user } = this.state;
if (user && user.items) {
const found = Object.keys(this.getUserItems()).some(
(key) => itemId === key,
);
return !found;
}
return true;
}

saveBtnClickHandler() {
trackEvent(
'ui',
Expand All @@ -482,15 +518,7 @@ BookLibService.Borrow(id) {
? 'saved'
: 'new',
);

if (!this.checkItemsLimit()) {
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();
Expand All @@ -510,7 +538,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);
Expand Down Expand Up @@ -582,14 +609,22 @@ BookLibService.Borrow(id) {
}

async openSavedItemsPane() {
await this.fetchSavedItems(async (items) => {
await this.populateItemsInSavedPane(items);
});
}

async fetchSavedItems(callbackFunc) {
await this.setState({
isFetchingItems: true,
});
this.fetchItems(true).then(async (items) => {
await this.setState({
isFetchingItems: false,
});
await this.populateItemsInSavedPane(items);
if (callbackFunc) {
callbackFunc(items);
}
});
}

Expand Down Expand Up @@ -624,7 +659,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
Expand Down Expand Up @@ -840,7 +875,7 @@ BookLibService.Borrow(id) {
}

// Save current item to storage
async saveItem() {
async saveItem(isManual = false) {
if (
!window.user &&
!window.localStorage[LocalStorageKeys.LOGIN_AND_SAVE_MESSAGE_SEEN] &&
Expand All @@ -858,7 +893,16 @@ BookLibService.Borrow(id) {
}
trackEvent('ui', LocalStorageKeys.LOGIN_AND_SAVE_MESSAGE_SEEN, 'local');
}
var isNewItem = !this.state.currentItem.id;
var isNewItem = this.isNewItem(this.state.currentItem.id);
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}`,
);
if (preventedSaving) {
if (isManual) this.alertItemsLimit();
return;
}
this.state.currentItem.id =
this.state.currentItem.id || 'item-' + generateRandomId();
await this.setState({
Expand All @@ -881,6 +925,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();
}
}

Expand Down Expand Up @@ -1058,14 +1103,7 @@ BookLibService.Borrow(id) {
}

async itemForkBtnClickHandler(item) {
if (!this.checkItemsLimit()) {
mixpanel.track({
event: 'Free Limit',
category: '3 diagrams limit',
label: 'Fork',
});
return;
}
if (this.alertAndTrackIfExceedItemsLimit('Fork', 1)) return;

await this.toggleSavedItemsPane();
setTimeout(() => {
Expand All @@ -1075,15 +1113,7 @@ BookLibService.Borrow(id) {

async newBtnClickHandler() {
mixpanel.track({ event: 'newBtnClick', category: 'ui' });

if (!this.checkItemsLimit()) {
mixpanel.track({
event: 'Free Limit',
category: '3 diagrams limit',
label: 'New',
});
return;
}
if (this.alertAndTrackIfExceedItemsLimit('New', 1)) return;

if (this.state.unsavedEditCount) {
var shouldDiscard = confirm(
Expand Down Expand Up @@ -1204,14 +1234,7 @@ BookLibService.Borrow(id) {
}

exportBtnClickHandler(e) {
if (!this.checkItemsLimit()) {
mixpanel.track({
event: 'Free Limit',
category: '3 diagrams limit',
label: 'Fork',
});
return;
}
if (this.alertAndTrackIfExceedItemsLimit('Export')) return;

this.exportItems();
e.preventDefault();
Expand Down Expand Up @@ -1248,6 +1271,7 @@ BookLibService.Borrow(id) {
}

mergeImportedItems(items) {
if (this.alertAndTrackIfExceedItemsLimit('Merge', items.length)) return;
var existingItemIds = [];
var toMergeItems = {};
const d = deferred();
Expand Down Expand Up @@ -1300,6 +1324,10 @@ BookLibService.Borrow(id) {
* Called from inside ask-to-import-modal
*/
importCreationsAndSettingsIntoApp() {
if (
this.alertAndTrackIfExceedItemsLimit('Import', this.oldSavedItems.length)
)
return;
this.mergeImportedItems(this.oldSavedItems).then(() => {
trackEvent('fn', 'oldItemsImported');
this.dontAskToImportAnymore();
Expand Down
5 changes: 3 additions & 2 deletions src/components/subscription/SubscriptionAction.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 <CancellationLink cancelUrl={subscription.cancel_url} />;
}
return <DisabledUpgradeLink upgradeBtnName={props.upgradeBtnName} />;
Expand Down
3 changes: 1 addition & 2 deletions src/components/subscription/UpgradeLink.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import planService from '../../services/planService';
import userService from '../../services/user_service';

const UpgradeLink = (props) => {
const checkout = (e) => {
e.preventDefault();
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,
Expand Down
1 change: 1 addition & 0 deletions src/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ import { log } from './utils';
merge: true,
},
);
//user.items value source
const user = doc.data();
Object.assign(window.user, user);
return user;
Expand Down
Loading