mirror of
https://github.com/chinchang/web-maker.git
synced 2025-08-01 03:00:09 +02:00
add working pro modal n checkout
This commit is contained in:
@@ -133,9 +133,9 @@ export default class Footer extends Component {
|
|||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
<Button
|
<Button
|
||||||
onClick={this.props.getProBtnClickHandler}
|
onClick={this.props.proBtnClickHandler}
|
||||||
data-event-category="ui"
|
data-event-category="ui"
|
||||||
data-event-action="getProFooterBtnClick"
|
data-event-action="proFooterBtnClick"
|
||||||
class="footer__link ml-1 hint--rounded hint--top-right hide-on-mobile support-link"
|
class="footer__link ml-1 hint--rounded hint--top-right hide-on-mobile support-link"
|
||||||
aria-label={i18n._(
|
aria-label={i18n._(
|
||||||
t`Be a PRO and get some advanced superpowers!`
|
t`Be a PRO and get some advanced superpowers!`
|
||||||
|
35
src/components/Pro.jsx
Normal file
35
src/components/Pro.jsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { useEffect, useState } from 'preact/hooks';
|
||||||
|
import { ProBadge } from './ProBadge';
|
||||||
|
import { HStack, Stack, VStack } from './Stack';
|
||||||
|
import Switch from './Switch';
|
||||||
|
import { alertsService } from '../notifications';
|
||||||
|
import { A, Button } from './common';
|
||||||
|
import { useCheckout } from '../hooks/useCheckout';
|
||||||
|
|
||||||
|
export function Pro({ user }) {
|
||||||
|
const hasCheckoutLoaded = useCheckout();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (hasCheckoutLoaded) {
|
||||||
|
window.LemonSqueezy.Setup({
|
||||||
|
eventHandler: e => {
|
||||||
|
console.log('eventHandler', e);
|
||||||
|
if (e.event === 'Checkout.Success') {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
window.LemonSqueezy.Refresh();
|
||||||
|
}
|
||||||
|
}, [hasCheckoutLoaded]);
|
||||||
|
return (
|
||||||
|
<VStack gap={4} align="stretch">
|
||||||
|
<A
|
||||||
|
class="btn lemonsqueezy-button"
|
||||||
|
href={`https://web-maker.lemonsqueezy.com/checkout/buy/1601bc00-9623-435b-b1de-2a70a2445c13?embed=1&checkout[custom][userId]=${user.uid}`}
|
||||||
|
>
|
||||||
|
Go <ProBadge />
|
||||||
|
</A>
|
||||||
|
</VStack>
|
||||||
|
);
|
||||||
|
}
|
@@ -4,12 +4,15 @@ import { HStack, Stack, VStack } from './Stack';
|
|||||||
import Switch from './Switch';
|
import Switch from './Switch';
|
||||||
import { itemService } from '../itemService';
|
import { itemService } from '../itemService';
|
||||||
import { alertsService } from '../notifications';
|
import { alertsService } from '../notifications';
|
||||||
|
import { Button } from './common';
|
||||||
|
|
||||||
const FREE_PUBLIC_ITEM_COUNT = 1;
|
const FREE_PUBLIC_ITEM_COUNT = 1;
|
||||||
|
const BASE_URL = location.origin;
|
||||||
|
|
||||||
export function Share({ user, item, onVisibilityChange }) {
|
export function Share({ user, item, onVisibilityChange }) {
|
||||||
const [publicItemCount, setPublicItemCount] = useState(0);
|
const [publicItemCount, setPublicItemCount] = useState(0);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!user) return;
|
||||||
window.db.getPublicItemCount(user.uid).then(c => {
|
window.db.getPublicItemCount(user.uid).then(c => {
|
||||||
setPublicItemCount(c);
|
setPublicItemCount(c);
|
||||||
console.log(c);
|
console.log(c);
|
||||||
@@ -25,7 +28,6 @@ export function Share({ user, item, onVisibilityChange }) {
|
|||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`http://127.0.0.1:5001/web-maker-app/us-central1/toggleVisibility?token=${token}&itemId=${item.id}`
|
`http://127.0.0.1:5001/web-maker-app/us-central1/toggleVisibility?token=${token}&itemId=${item.id}`
|
||||||
);
|
);
|
||||||
console.log(res.status);
|
|
||||||
|
|
||||||
if (res.status >= 200 && res.status < 400) {
|
if (res.status >= 200 && res.status < 400) {
|
||||||
setPublicItemCount(publicItemCount + 1);
|
setPublicItemCount(publicItemCount + 1);
|
||||||
@@ -41,31 +43,45 @@ export function Share({ user, item, onVisibilityChange }) {
|
|||||||
alertsService.add('Visiblity set to private');
|
alertsService.add('Visiblity set to private');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const copyUrl = () => {
|
||||||
|
navigator.clipboard.writeText(`${BASE_URL}/create/${item.id}`);
|
||||||
|
alertsService.add('URL copied to clipboard');
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<VStack gap={4} align="stretch">
|
<VStack gap={4} align="stretch">
|
||||||
<VStack gap={1} align="stretch">
|
<VStack gap={1} align="stretch">
|
||||||
<Switch checked={val} onChange={onChange}>
|
<Switch
|
||||||
Go public?
|
checked={val}
|
||||||
|
onChange={onChange}
|
||||||
|
labels={['Private', 'Public']}
|
||||||
|
>
|
||||||
|
Access
|
||||||
</Switch>
|
</Switch>
|
||||||
{item.isPublic && (
|
{item.isPublic && (
|
||||||
<p>
|
<p>
|
||||||
Public at{' '}
|
Public at{' '}
|
||||||
<a href={`https://webmaker.app/create/${item.id}`} target="_blank">
|
<a href={`${BASE_URL}/create/${item.id}`} target="_blank">
|
||||||
https://webmaker.app/create/{item.id}
|
{BASE_URL}/create/{item.id}
|
||||||
</a>
|
</a>{' '}
|
||||||
|
<Button class="btn btn--dark" onClick={copyUrl}>
|
||||||
|
Copy
|
||||||
|
</Button>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</VStack>
|
</VStack>
|
||||||
|
|
||||||
<VStack gap={1} align="stretch">
|
{!user?.isPro ? (
|
||||||
<p>
|
<VStack gap={1} align="stretch">
|
||||||
You have {FREE_PUBLIC_ITEM_COUNT - publicItemCount}/
|
<p>
|
||||||
{FREE_PUBLIC_ITEM_COUNT} public creations left.
|
You have {FREE_PUBLIC_ITEM_COUNT - publicItemCount}/
|
||||||
</p>
|
{FREE_PUBLIC_ITEM_COUNT} public creations left.
|
||||||
<p>
|
</p>
|
||||||
For unlimited public creations, upgrade to <ProBadge />
|
<p>
|
||||||
</p>
|
For unlimited public creations, upgrade to <ProBadge />
|
||||||
</VStack>
|
</p>
|
||||||
|
</VStack>
|
||||||
|
) : null}
|
||||||
</VStack>
|
</VStack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -71,6 +71,7 @@ import { I18nProvider } from '@lingui/react';
|
|||||||
import { Assets } from './Assets.jsx';
|
import { Assets } from './Assets.jsx';
|
||||||
import { LocalStorageKeys } from '../constants.js';
|
import { LocalStorageKeys } from '../constants.js';
|
||||||
import { Share } from './Share.jsx';
|
import { Share } from './Share.jsx';
|
||||||
|
import { Pro } from './Pro.jsx';
|
||||||
|
|
||||||
if (module.hot) {
|
if (module.hot) {
|
||||||
require('preact/debug');
|
require('preact/debug');
|
||||||
@@ -121,7 +122,8 @@ export default class App extends Component {
|
|||||||
isCreateNewModalOpen: false,
|
isCreateNewModalOpen: false,
|
||||||
isCommandPaletteOpen: false,
|
isCommandPaletteOpen: false,
|
||||||
isAssetsOpen: false,
|
isAssetsOpen: false,
|
||||||
isShareModalOpen: false
|
isShareModalOpen: false,
|
||||||
|
isProModalOpen: false
|
||||||
};
|
};
|
||||||
this.state = {
|
this.state = {
|
||||||
isSavedItemPaneOpen: false,
|
isSavedItemPaneOpen: false,
|
||||||
@@ -1131,6 +1133,10 @@ export default class App extends Component {
|
|||||||
trackEvent('ui', 'notificationButtonClick', version);
|
trackEvent('ui', 'notificationButtonClick', version);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
proBtnClickHandler() {
|
||||||
|
this.setState({ isProModalOpen: true });
|
||||||
|
trackEvent('ui', 'proBtnClick');
|
||||||
|
}
|
||||||
codepenBtnClickHandler(e) {
|
codepenBtnClickHandler(e) {
|
||||||
if (this.state.currentItem.cssMode === CssModes.ACSS) {
|
if (this.state.currentItem.cssMode === CssModes.ACSS) {
|
||||||
alert(
|
alert(
|
||||||
@@ -1723,6 +1729,7 @@ export default class App extends Component {
|
|||||||
onJs13KDownloadBtnClick={this.js13KDownloadBtnClickHandler.bind(
|
onJs13KDownloadBtnClick={this.js13KDownloadBtnClickHandler.bind(
|
||||||
this
|
this
|
||||||
)}
|
)}
|
||||||
|
proBtnClickHandler={this.proBtnClickHandler.bind(this)}
|
||||||
hasUnseenChangelog={this.state.hasUnseenChangelog}
|
hasUnseenChangelog={this.state.hasUnseenChangelog}
|
||||||
codeSize={this.state.codeSize}
|
codeSize={this.state.codeSize}
|
||||||
/>
|
/>
|
||||||
@@ -1817,6 +1824,12 @@ export default class App extends Component {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
<Modal
|
||||||
|
show={this.state.isProModalOpen}
|
||||||
|
closeHandler={() => this.setState({ isProModalOpen: false })}
|
||||||
|
>
|
||||||
|
<Pro user={this.state.user} />
|
||||||
|
</Modal>
|
||||||
<HelpModal
|
<HelpModal
|
||||||
show={this.state.isHelpModalOpen}
|
show={this.state.isHelpModalOpen}
|
||||||
closeHandler={() => this.setState({ isHelpModalOpen: false })}
|
closeHandler={() => this.setState({ isHelpModalOpen: false })}
|
||||||
|
@@ -4,11 +4,15 @@ import { trackEvent } from '../analytics';
|
|||||||
class Clickable extends Component {
|
class Clickable extends Component {
|
||||||
handleClick(e) {
|
handleClick(e) {
|
||||||
const el = e.currentTarget;
|
const el = e.currentTarget;
|
||||||
trackEvent(
|
if (el.getAttribute('data-event-category')) {
|
||||||
el.getAttribute('data-event-category'),
|
trackEvent(
|
||||||
el.getAttribute('data-event-action')
|
el.getAttribute('data-event-category'),
|
||||||
);
|
el.getAttribute('data-event-action')
|
||||||
this.props.onClick(e);
|
);
|
||||||
|
}
|
||||||
|
if (this.props.onClick) {
|
||||||
|
this.props.onClick(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
/* eslint-disable no-unused-vars */
|
/* eslint-disable no-unused-vars */
|
||||||
|
20
src/hooks/useCheckout.js
Normal file
20
src/hooks/useCheckout.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
function useCheckout() {
|
||||||
|
const [hasVendorScriptLoaded, setHasVendorScriptLoaded] = useState();
|
||||||
|
useEffect(() => {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.src = 'https://app.lemonsqueezy.com/js/lemon.js';
|
||||||
|
script.async = 'true';
|
||||||
|
script.defer = 'true';
|
||||||
|
script.addEventListener('load', () => {
|
||||||
|
window.createLemonSqueezy();
|
||||||
|
setHasVendorScriptLoaded(true);
|
||||||
|
});
|
||||||
|
document.body.appendChild(script);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return hasVendorScriptLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { useCheckout };
|
@@ -376,6 +376,7 @@ a > svg {
|
|||||||
float: right;
|
float: right;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
margin-inline-start: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
|
Reference in New Issue
Block a user