mobile: pricing fixes

This commit is contained in:
Ammar Ahmed
2025-09-23 10:13:39 +05:00
committed by Abdullah Atta
parent 58c1f5e3ca
commit a49bc92a13
11 changed files with 702 additions and 980 deletions

View File

@@ -141,6 +141,12 @@ const PayWall = (props: NavigationProps<"PayWall">) => {
}; };
}, []); }, []);
const is5YearPlanSelected = (
isGithubRelease
? (pricingPlans.selectedProduct as Plan)?.period
: (pricingPlans.selectedProduct as RNIap.Product)?.productId
)?.includes("5");
return ( return (
<SafeAreaView <SafeAreaView
style={{ style={{
@@ -603,7 +609,11 @@ After trying all the privacy security oriented note taking apps, for the price a
goBack={() => { goBack={() => {
setStep(Steps.select); setStep(Steps.select);
}} }}
goNext={() => { goNext={(product) => {
pricingPlans.selectProduct(
(product as RNIap.Subscription).productId ||
(product as Plan).period
);
setStep(Steps.finish); setStep(Steps.finish);
}} }}
/> />
@@ -647,7 +657,11 @@ After trying all the privacy security oriented note taking apps, for the price a
> >
<AppIcon color={colors.primary.accent} name="check" size={30} /> <AppIcon color={colors.primary.accent} name="check" size={30} />
</View> </View>
<Heading>{strings.thankYouForSubscribing()}</Heading> <Heading>
{is5YearPlanSelected
? strings.thankYouForPurchase()
: strings.thankYouForSubscribing()}
</Heading>
<Paragraph <Paragraph
style={{ style={{
textAlign: "center" textAlign: "center"

View File

@@ -248,7 +248,8 @@ export const Items = ({
testID={"icon-" + item.id} testID={"icon-" + item.id}
style={{ style={{
width: columnItemWidth - 8, width: columnItemWidth - 8,
alignSelf: "flex-start" alignSelf: "flex-start",
gap: DefaultAppStyles.GAP_VERTICAL_SMALL
}} }}
> >
<View <View
@@ -259,7 +260,9 @@ export const Items = ({
alignItems: "center", alignItems: "center",
borderWidth: 1, borderWidth: 1,
borderRadius: defaultBorderRadius, borderRadius: defaultBorderRadius,
borderColor: colors.primary.border, borderColor: item.checked
? item.activeColor || colors.primary.accent
: colors.primary.border,
overflow: "hidden" overflow: "hidden"
}} }}
> >
@@ -354,8 +357,8 @@ export const Items = ({
paginationStyle={{ paginationStyle={{
position: "relative", position: "relative",
marginHorizontal: 2, marginHorizontal: 2,
marginBottom: 0, marginBottom: -10,
marginTop: 0 marginTop: 10
}} }}
contentContainerStyle={{ contentContainerStyle={{
justifyContent: "flex-start", justifyContent: "flex-start",

View File

@@ -40,7 +40,9 @@ export const BuyPlan = (props: {
productId: string; productId: string;
canActivateTrial?: boolean; canActivateTrial?: boolean;
goBack: () => void; goBack: () => void;
goNext: () => void; goNext: (
product: Plan | RNIap.Subscription | RNIap.Product | undefined
) => void;
}) => { }) => {
const { colors } = useThemeColors(); const { colors } = useThemeColors();
const [checkoutUrl, setCheckoutUrl] = useState<string>(); const [checkoutUrl, setCheckoutUrl] = useState<string>();
@@ -48,7 +50,7 @@ export const BuyPlan = (props: {
planId: props.planId, planId: props.planId,
productId: props.productId, productId: props.productId,
onBuy: () => { onBuy: () => {
props.goNext(); props.goNext(pricingPlans.selectedProduct);
} }
}); });
@@ -78,7 +80,7 @@ export const BuyPlan = (props: {
try { try {
const data = JSON.parse(message.nativeEvent.data); const data = JSON.parse(message.nativeEvent.data);
if (data.success) { if (data.success) {
props.goNext(); props.goNext(pricingPlans.selectedProduct);
} }
} catch (e) {} } catch (e) {}
}} }}
@@ -139,7 +141,11 @@ export const BuyPlan = (props: {
> >
<Heading color={colors.primary.paragraph} size={AppFontSize.sm}> <Heading color={colors.primary.paragraph} size={AppFontSize.sm}>
{strings.dueToday()}{" "} {strings.dueToday()}{" "}
{pricingPlans.userCanRequestTrial ? ( {pricingPlans.hasTrialOffer(
props.planId,
(pricingPlans?.selectedProduct as RNIap.Product)?.productId ||
(pricingPlans?.selectedProduct as Plan)?.period
) ? (
<Text <Text
style={{ style={{
color: colors.primary.accent color: colors.primary.accent
@@ -151,7 +157,11 @@ export const BuyPlan = (props: {
</Heading> </Heading>
<Paragraph color={colors.primary.paragraph}> <Paragraph color={colors.primary.paragraph}>
{pricingPlans.userCanRequestTrial {pricingPlans.hasTrialOffer(
props.planId,
(pricingPlans?.selectedProduct as RNIap.Product)?.productId ||
(pricingPlans?.selectedProduct as Plan)?.period
)
? "FREE" ? "FREE"
: pricingPlans.getStandardPrice( : pricingPlans.getStandardPrice(
pricingPlans.selectedProduct as RNIap.Subscription pricingPlans.selectedProduct as RNIap.Subscription

View File

@@ -70,7 +70,6 @@ const SheetWrapper = ({
backgroundColor: colors.primary.background, backgroundColor: colors.primary.background,
zIndex: 10, zIndex: 10,
paddingTop: 5, paddingTop: 5,
paddingBottom: 0,
borderTopRightRadius: 15, borderTopRightRadius: 15,
borderTopLeftRadius: 15, borderTopLeftRadius: 15,
alignSelf: "center", alignSelf: "center",
@@ -78,9 +77,10 @@ const SheetWrapper = ({
borderBottomLeftRadius: 0, borderBottomLeftRadius: 0,
...getContainerBorder(colors.primary.border, 0.5), ...getContainerBorder(colors.primary.border, 0.5),
borderBottomWidth: 0, borderBottomWidth: 0,
paddingBottom: isGestureNavigationEnabled paddingBottom:
? insets.bottom isGestureNavigationEnabled && Platform.OS === "android"
: insets.bottom || 48 ? insets.bottom
: 0
}; };
}, [ }, [
colors.primary.background, colors.primary.background,
@@ -165,8 +165,8 @@ const SheetWrapper = ({
<View <View
style={{ style={{
height: height:
Platform.OS === "ios" && insets.bottom !== 0 Platform.OS === "ios" || insets.bottom !== 0
? insets.bottom + 5 ? insets.bottom
: 20 : 20
}} }}
/> />

View File

@@ -17,20 +17,20 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React, { useState } from "react"; import React, { useState } from "react";
import { LayoutAnimation, View } from "react-native"; import { LayoutAnimation, View } from "react-native";
import { MMKV } from "../../common/database/mmkv"; import { MMKV } from "../../common/database/mmkv";
import { eSendEvent, presentSheet } from "../../services/event-manager"; import { eSendEvent, presentSheet } from "../../services/event-manager";
import { useThemeColors } from "@notesnook/theme";
import { eCloseSheet } from "../../utils/events"; import { eCloseSheet } from "../../utils/events";
import { AppFontSize } from "../../utils/size"; import { AppFontSize } from "../../utils/size";
import { DefaultAppStyles } from "../../utils/styles";
import { sleep } from "../../utils/time"; import { sleep } from "../../utils/time";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
import Heading from "../ui/typography/heading"; import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph"; import Paragraph from "../ui/typography/paragraph";
import walkthroughs, { TStep } from "./walkthroughs"; import walkthroughs, { TStep } from "./walkthroughs";
import { strings } from "@notesnook/intl";
import { DefaultAppStyles } from "../../utils/styles";
export const Walkthrough = ({ export const Walkthrough = ({
steps, steps,
canSkip = true canSkip = true
@@ -65,7 +65,7 @@ export const Walkthrough = ({
textAlign: "center" textAlign: "center"
}} }}
> >
Notesnook Free plan activated {step.title}
</Heading> </Heading>
) : null} ) : null}
{step.text ? ( {step.text ? (
@@ -151,7 +151,7 @@ Walkthrough.init = async () => {
}; };
Walkthrough.present = async ( Walkthrough.present = async (
id: "notebooks" | "trialstarted" | "emailconfirmed" | "prouser", id: "emailconfirmed" | "prouser",
canSkip = true, canSkip = true,
nopersist?: boolean nopersist?: boolean
) => { ) => {
@@ -162,7 +162,7 @@ Walkthrough.present = async (
if (walkthroughState[id]) return; if (walkthroughState[id]) return;
Walkthrough.update(id); Walkthrough.update(id);
} }
const walkthrough = walkthroughs[id]; const walkthrough = walkthroughs[id]();
if (!walkthrough) return; if (!walkthrough) return;
presentSheet({ presentSheet({
component: <Walkthrough canSkip={canSkip} steps={walkthrough.steps} />, component: <Walkthrough canSkip={canSkip} steps={walkthrough.steps} />,

View File

@@ -60,282 +60,7 @@ export type TStep = {
}; };
}; };
const NotebookWelcome = () => { const emailconfirmed: () => { id: string; steps: TStep[] } = () => ({
const { colors } = useThemeColors();
const data = useRotator([
{
title: strings.workAndOffice(),
description: strings.workAndOfficeDesc(),
count: 2
},
{
title: strings.schoolWork(),
description: strings.schoolWorkDesc(),
count: 5
},
{
title: strings.recipes(),
description: strings.recipesDesc(),
count: 10
}
]);
return (
<View
style={{
width: "100%",
padding: DefaultAppStyles.GAP,
backgroundColor: colors.secondary.background,
borderRadius: 10,
...getContainerBorder(colors.secondary.background)
}}
>
<View
style={{
padding: DefaultAppStyles.GAP,
width: "100%",
backgroundColor: colors.primary.background,
...getElevationStyle(3),
borderRadius: 10,
marginVertical: DefaultAppStyles.GAP
}}
>
<Heading size={AppFontSize.md} color={colors.primary.heading}>
{data?.title}
</Heading>
<Paragraph>{data?.description}</Paragraph>
<Paragraph
style={{
marginTop: DefaultAppStyles.GAP_VERTICAL_SMALL
}}
size={AppFontSize.xs}
color={colors.secondary.paragraph}
>
{strings.dataTypesCamelCase.notebook()} - {data?.count}{" "}
{strings.dataTypesPlural.note()}
</Paragraph>
</View>
</View>
);
};
const notebooks: { id: string; steps: TStep[] } = {
id: "notebooks",
steps: [
{
title: strings.notebooks(),
text: strings.boostProductivityNotebook(),
walkthroughItem: () => <NotebookWelcome />,
button: {
type: "next",
title: strings.next()
}
},
{
title: strings.notebookNotes(),
text: strings.notebookNotesDesc(),
walkthroughItem: (colors: any) => (
<View
style={{
width: "100%",
padding: DefaultAppStyles.GAP,
backgroundColor: colors.secondary.background,
borderRadius: 10,
...getContainerBorder(colors.secondary.background)
}}
>
<View
style={{
padding: DefaultAppStyles.GAP,
width: "100%",
backgroundColor: colors.primary.background,
...getElevationStyle(3),
borderRadius: 10,
marginVertical: 12
}}
>
<Heading size={AppFontSize.md} color={colors.primary.heading}>
{strings.workAndOffice()}
</Heading>
<Paragraph>{strings.workAndOfficeDesc()}</Paragraph>
<Paragraph
style={{
marginTop: DefaultAppStyles.GAP_VERTICAL_SMALL
}}
size={AppFontSize.xs}
color={colors.secondary.paragraph}
>
{strings.notes(2)}
</Paragraph>
</View>
<View
style={{
padding: DefaultAppStyles.GAP,
width: "90%",
backgroundColor: colors.primary.background,
borderRadius: 10,
alignSelf: "flex-end",
marginBottom: DefaultAppStyles.GAP_VERTICAL
}}
>
<Paragraph color={colors.primary.accent}>
<Icon
color={colors.primary.accent}
size={AppFontSize.sm}
name="bookmark"
/>{" "}
{strings.tasks()}
</Paragraph>
</View>
<View
style={{
padding: DefaultAppStyles.GAP,
paddingVertical: DefaultAppStyles.GAP_VERTICAL,
width: "80%",
backgroundColor: colors.primary.background,
borderRadius: defaultBorderRadius,
alignSelf: "flex-end",
marginBottom: DefaultAppStyles.GAP_VERTICAL
}}
>
<Paragraph size={AppFontSize.xs}>
<Icon
color={colors.primary.icon}
size={AppFontSize.sm}
name="note"
/>{" "}
{strings.taskAValue()}
</Paragraph>
</View>
<View
style={{
padding: DefaultAppStyles.GAP,
width: "80%",
backgroundColor: colors.primary.background,
borderRadius: defaultBorderRadius,
paddingVertical: DefaultAppStyles.GAP_VERTICAL,
alignSelf: "flex-end",
marginBottom: DefaultAppStyles.GAP_VERTICAL
}}
>
<Paragraph size={AppFontSize.xs}>
<Icon
color={colors.primary.icon}
size={AppFontSize.sm}
name="note"
/>{" "}
{strings.taskBValue()}
</Paragraph>
</View>
<View
style={{
padding: DefaultAppStyles.GAP,
width: "90%",
backgroundColor: colors.primary.background,
borderRadius: 10,
alignSelf: "flex-end",
marginBottom: DefaultAppStyles.GAP_VERTICAL
}}
>
<Paragraph color={colors.primary.accent}>
<Icon
color={colors.primary.accent}
size={AppFontSize.sm}
name="bookmark"
/>{" "}
{strings.meetings()}
</Paragraph>
</View>
</View>
),
button: {
type: "next",
title: strings.next()
}
},
{
title: strings.easyAccess(),
text: strings.easyAccessDesc(),
walkthroughItem: () => (
<View
style={{
paddingHorizontal: 20,
paddingVertical: DefaultAppStyles.GAP_VERTICAL
}}
>
{/* <PinItem
isPlaceholder={true}
item={
{
title: strings.tasks(),
type: "notebook"
} as Notebook
}
onPress={() => {}}
/>
<PinItem
isPlaceholder={true}
item={
{
title: strings.workAndOffice(),
type: "notebook"
} as Notebook
}
onPress={() => {}}
/> */}
</View>
),
button: {
type: "done",
title: strings.addFirstNotebook(),
action: () => {
eSendEvent(eOpenAddNotebookDialog);
}
}
}
]
};
const trialstarted: { id: string; steps: TStep[] } = {
id: "trialstarted",
steps: [
{
title: strings.trialStarted(),
text: strings.trialStartedDesc(),
walkthroughItem: (colors) => (
<SvgView src={LAUNCH_ROCKET(colors.primary.paragraph)} />
),
button: {
type: "next",
title: strings.next()
}
},
{
title: strings.joinTheCause(),
text: strings.meetPrivacyMinded(),
walkthroughItem: (colors) => (
<SvgView src={COMMUNITY_SVG(colors.primary.paragraph)} />
),
button: {
type: "done",
title: strings.continue()
},
actionButton: {
text: strings.joinDiscord(),
action: () => {
Linking.openURL("https://discord.gg/zQBK97EE22").catch(() => {
/* empty */
});
}
}
}
]
};
const emailconfirmed: { id: string; steps: TStep[] } = {
id: "emailconfirmed", id: "emailconfirmed",
steps: [ steps: [
{ {
@@ -350,86 +75,29 @@ const emailconfirmed: { id: string; steps: TStep[] } = {
} }
} }
] ]
}; });
const Support = () => { const prouser: () => { id: string; steps: TStep[] } = () => {
return ( return {
<View id: "prouser",
style={{ steps: [
width: "100%", {
alignItems: "center" title: strings.welcomeToPlan(
}} planToDisplayName(
> useUserStore.getState().user?.subscription?.plan as SubscriptionPlan
<Heading>{strings.prioritySupport()}</Heading> )
<Paragraph ),
style={{ text: strings.thankYouPrivacy(),
textAlign: "center" walkthroughItem: (colors) => (
}} <AppIcon name="check" color={colors.primary.accent} size={50} />
size={AppFontSize.md} ),
> button: {
{strings.weAreAlwaysListening()} type: "done",
</Paragraph> title: strings.continue()
<Seperator /> }
<Button
style={{
justifyContent: "flex-start",
marginBottom: DefaultAppStyles.GAP_VERTICAL,
width: "90%"
}}
onPress={() => {
Linking.openURL("https://discord.gg/zQBK97EE22").catch(() => {
/* empty */
});
}}
icon="discord"
type="secondary"
title={strings.joinDiscord()}
/>
<Button
style={{
justifyContent: "flex-start",
marginBottom: DefaultAppStyles.GAP_VERTICAL,
width: "90%"
}}
icon="bug"
type="secondary"
title={strings.reportAnIssue()}
/>
<Button
style={{
justifyContent: "flex-start",
marginBottom: DefaultAppStyles.GAP_VERTICAL,
width: "90%"
}}
icon="email"
type="secondary"
title={strings.emailSupport()}
/>
</View>
);
};
const prouser: { id: string; steps: TStep[] } = {
id: "prouser",
steps: [
{
title: strings.welcomeToPlan(
planToDisplayName(
useUserStore.getState().user?.subscription?.plan as SubscriptionPlan
)
),
text: strings.thankYouPrivacy(),
walkthroughItem: (colors) => (
<AppIcon name="check" color={colors.primary.accent} size={50} />
),
button: {
type: "done",
title: strings.continue()
} }
} ]
] };
}; };
export default { notebooks, trialstarted, emailconfirmed, prouser }; export default { emailconfirmed, prouser };

View File

@@ -401,6 +401,7 @@ const usePricingPlans = (options?: PricingPlansOptions) => {
obfuscatedAccountIdAndroid: user.id, obfuscatedAccountIdAndroid: user.id,
obfuscatedProfileIdAndroid: user.id, obfuscatedProfileIdAndroid: user.id,
sku: product.productId, sku: product.productId,
skus: [product.productId],
quantity: 1 quantity: 1
}); });
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -2579,5 +2579,6 @@ Use this if changes from other devices are not appearing on this device. This wi
unlimited: () => t`Unlimited`, unlimited: () => t`Unlimited`,
fiveYearPlan: () => t`5 year plan (One time purchase)`, fiveYearPlan: () => t`5 year plan (One time purchase)`,
educationPlan: () => t`Education plan`, educationPlan: () => t`Education plan`,
welcomeToPlan: (plan: string) => t`Welcome to Notesnook ${plan}` welcomeToPlan: (plan: string) => t`Welcome to Notesnook ${plan}`,
thankYouForPurchase: () => t`Thank you for the purchase`
}; };