mobile: update ui for paywall flow

This commit is contained in:
Ammar Ahmed
2026-04-21 11:44:13 +05:00
parent 2e09a691e7
commit 37e7d44587
15 changed files with 1621 additions and 1066 deletions

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,24 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
export const Steps = {
select: 1,
buy: 2,
finish: 3,
buyWeb: 4
};

View File

@@ -0,0 +1,181 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { getFeaturesTable } from "@notesnook/common";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React from "react";
import { ScrollView, useWindowDimensions, View } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
//@ts-ignore
import usePricingPlans from "../../hooks/use-pricing-plans";
import { AppFontSize } from "../../utils/size";
import { Button } from "../ui/button";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { Steps } from "./common";
export const ComparePlans = React.memo(
(props: {
pricingPlans?: ReturnType<typeof usePricingPlans>;
setStep: (step: number) => void;
}) => {
const { colors } = useThemeColors();
const { width } = useWindowDimensions();
const isTablet = width > 600;
return (
<ScrollView
horizontal
style={{
width: isTablet ? "100%" : undefined
}}
contentContainerStyle={{
flexDirection: "column"
}}
>
<View
style={{
flexDirection: "row",
alignItems: "center",
width: "100%",
gap: 10
}}
>
{["Features", "Free", "Essential", "Pro", "Believer"].map(
(plan, index) => (
<View
key={plan}
style={{
width: index === 0 ? 150 : 120,
paddingHorizontal: 16,
paddingVertical: 12,
backgroundColor:
index === 0 ? colors.secondary.background : undefined,
borderBottomWidth: index === 0 ? 1 : undefined,
borderBottomColor: colors.primary.border
}}
>
<Heading size={AppFontSize.sm}>{plan}</Heading>
</View>
)
)}
</View>
{getFeaturesTable().map((item, keyIndex) => {
return (
<View
key={`${item[0] + item[1]}`}
style={{
flexDirection: "row",
alignItems: "center",
width: "100%",
gap: 10
}}
>
{item.map((featureItem, index) => (
<View
style={{
width: index === 0 ? 150 : 120,
paddingHorizontal: 16,
paddingVertical: 12,
backgroundColor:
index === 0 ? colors.secondary.background : undefined,
borderBottomWidth: index === 0 ? 1 : undefined,
borderBottomColor: colors.primary.border
}}
key={item[0] + index}
>
{typeof featureItem === "string" ? (
<Heading size={AppFontSize.sm}>
{featureItem as string}
</Heading>
) : (
<>
{typeof featureItem.caption === "string" ||
typeof featureItem.caption === "number" ? (
<Paragraph>
{featureItem.caption === "infinity"
? "∞"
: featureItem.caption}
</Paragraph>
) : typeof featureItem.caption === "boolean" ? (
<>
{featureItem.caption === true ? (
<Icon
color={colors.primary.accent}
size={AppFontSize.sm}
name="check"
/>
) : (
<Icon
size={AppFontSize.sm}
color={colors.static.red}
name="close"
/>
)}
</>
) : null}
</>
)}
</View>
))}
</View>
);
})}
<View
style={{
flexDirection: "row",
alignItems: "center",
width: "100%",
gap: 10
}}
>
{["features", "free", "essential", "pro", "believer"].map(
(plan, index) => (
<View
key={plan + "btn"}
style={{
width: index === 0 ? 150 : 120,
paddingHorizontal: 16,
paddingVertical: 8
}}
>
{plan !== "free" && plan !== "features" ? (
<Button
title={strings.select()}
type="accent"
fontSize={AppFontSize.xs}
onPress={() => {
props.pricingPlans?.selectPlan(plan);
props.setStep(Steps.buy);
}}
/>
) : null}
</View>
)
)}
</View>
</ScrollView>
);
},
() => true
);
ComparePlans.displayName = "ComparePlans";

View File

@@ -0,0 +1,73 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useThemeColors } from "@notesnook/theme";
import React, { useState } from "react";
import { TouchableOpacity, View } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
//@ts-ignore
import { AppFontSize } from "../../utils/size";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
export const FAQItem = (props: { question: string; answer: string }) => {
const [expanded, setExpanded] = useState(false);
const { colors } = useThemeColors();
return (
<TouchableOpacity
style={{
padding: 16,
backgroundColor: colors.secondary.background,
borderRadius: 10,
marginBottom: 10,
gap: 12
}}
activeOpacity={0.9}
onPress={() => {
setExpanded(!expanded);
}}
key={props.question}
>
<View
style={{
flexDirection: "row",
width: "100%",
justifyContent: "space-between"
}}
>
<Heading
style={{
flexShrink: 1
}}
size={AppFontSize.md}
>
{props.question}
</Heading>
<Icon
name={expanded ? "chevron-up" : "chevron-down"}
color={colors.secondary.icon}
size={AppFontSize.xxl}
/>
</View>
{expanded ? (
<Paragraph size={AppFontSize.md}>{props.answer}</Paragraph>
) : null}
</TouchableOpacity>
);
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,332 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { SKUResponse } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React, { useEffect, useState } from "react";
import {
ActivityIndicator,
Text,
useWindowDimensions,
View
} from "react-native";
import * as RNIap from "react-native-iap";
//@ts-ignore
import usePricingPlans, {
PlanOverView,
PricingPlan
} from "../../hooks/use-pricing-plans";
import PremiumService from "../../services/premium";
import { getElevationStyle } from "../../utils/elevation";
import { AppFontSize, defaultBorderRadius } from "../../utils/size";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { Steps } from "./common";
import { Radius, Spacing } from "../../common/design/spacing";
import AppIcon from "../ui/AppIcon";
import { Button } from "../ui/button";
export const PricingPlanCard = ({
plan,
pricingPlans,
annualBilling,
setStep
}: {
plan: PricingPlan;
pricingPlans?: ReturnType<typeof usePricingPlans>;
annualBilling?: boolean;
setStep: (step: number) => void;
}) => {
const { colors } = useThemeColors();
const [regionalDiscount, setRegionaDiscount] = useState<SKUResponse>();
const { width } = useWindowDimensions();
const isTablet = width > 600;
const product =
plan.subscriptions?.[
regionalDiscount?.sku ||
`notesnook.${plan.id}.${annualBilling ? "yearly" : "monthly"}`
];
const WebPlan = pricingPlans?.getWebPlan(
plan.id,
annualBilling ? "yearly" : "monthly"
);
const price = pricingPlans?.getPrice(
pricingPlans.isGithubRelease && WebPlan
? WebPlan
: (product as RNIap.Subscription),
pricingPlans.hasTrialOffer(plan.id, product?.productId) ? 1 : 0,
annualBilling
);
useEffect(() => {
if (pricingPlans?.isGithubRelease || !annualBilling) return;
pricingPlans
?.getRegionalDiscount(
plan.id,
pricingPlans.isGithubRelease
? (WebPlan?.period as string)
: `notesnook.${plan.id}.${annualBilling ? "yearly" : "monthly"}`
)
.then((value) => {
setRegionaDiscount(value);
});
}, [WebPlan?.period, annualBilling, plan.id, pricingPlans]);
useEffect(() => {
if (!annualBilling) {
setRegionaDiscount(undefined);
}
}, [annualBilling]);
const isSubscribed =
product?.productId &&
pricingPlans?.user?.subscription?.productId?.includes(plan.id) &&
pricingPlans.isSubscribed();
const isNotReady =
pricingPlans?.loadingPlans || (!price && !WebPlan?.price?.gross);
return (
<View
style={{
...getElevationStyle(3),
backgroundColor: colors.secondary.background,
borderWidth: 1,
borderColor: colors.primary.border,
borderRadius: Radius.LG,
padding: 16,
flexShrink: isTablet ? 1 : undefined,
flexDirection: "column",
justifyContent: "space-between",
gap: 6
}}
>
<View>
<View
style={{
flexDirection: "row",
alignItems: "center",
width: "100%",
justifyContent: "space-between",
marginBottom: Spacing.LEVEL_1
}}
>
<Heading size={AppFontSize.xl}>{plan.name} </Heading>
<View
style={{
flexDirection: "row",
alignItems: "center",
gap: Spacing.LEVEL_1
}}
>
{isSubscribed ? (
<View
style={{
backgroundColor: colors.primary.accent,
borderRadius: defaultBorderRadius,
padding: Spacing.LEVEL_0,
alignItems: "center",
justifyContent: "center",
alignSelf: "flex-start",
marginBottom: Spacing.LEVEL_1
}}
>
<Heading color={colors.static.white} size={AppFontSize.xxs}>
{strings.currentPlan()}
</Heading>
</View>
) : (
<>
{regionalDiscount?.discount || WebPlan?.discount ? (
<View
style={{
backgroundColor: colors.static.red,
borderRadius: 100,
padding: Spacing.LEVEL_0,
paddingHorizontal: Spacing.LEVEL_1,
justifyContent: "center",
alignSelf: "flex-start"
}}
>
<Heading color={colors.static.white} size={AppFontSize.xxs}>
{strings.percentOff(
`${regionalDiscount?.discount || WebPlan?.discount?.amount}`
)}
</Heading>
</View>
) : null}
{plan.recommended ? (
<View
style={{
backgroundColor: colors.static.orange,
borderRadius: 100,
padding: Spacing.LEVEL_0,
paddingHorizontal: Spacing.LEVEL_1
}}
>
<Text
style={{
color: colors.static.black,
fontSize: AppFontSize.xxs
}}
>
{strings.recommended()}
</Text>
</View>
) : null}
</>
)}
</View>
</View>
<Paragraph>{plan.description}</Paragraph>
<View
style={{
marginTop: Spacing.LEVEL_3
}}
>
{pricingPlans?.loadingPlans || (!price && !WebPlan?.price?.gross) ? (
<ActivityIndicator size="small" color={colors.primary.accent} />
) : (
<View>
<Heading size={24}>
{price ||
`${WebPlan?.price?.currency} ${WebPlan?.price?.gross}`}{" "}
<Paragraph color={colors.primary.paragraph} fontSize="SM">
/{strings.month()}
</Paragraph>
</Heading>
{!product && !WebPlan ? null : (
<Paragraph
color={colors.secondary.paragraph}
size={AppFontSize.xs}
>
{annualBilling
? strings.billedAnnually(
pricingPlans?.getStandardPrice(
(product || WebPlan) as any
) as string
)
: strings.billedMonthly(
pricingPlans?.getStandardPrice(
(product || WebPlan) as any
) as string
)}
</Paragraph>
)}
</View>
)}
</View>
<View
style={{
marginVertical: Spacing.LEVEL_4,
gap: Spacing.LEVEL_2
}}
>
{Object.keys(PlanOverView[plan.id as keyof typeof PlanOverView]).map(
(item) => (
<View
key={item + plan.id}
style={{
flexDirection: "row",
width: "100%",
justifyContent: "space-between",
borderBottomColor: colors.primary.border,
borderBottomWidth: 1,
paddingBottom: Spacing.LEVEL_2
}}
>
<View
style={{
flexDirection: "row",
gap: Spacing.LEVEL_1
}}
>
<AppIcon
name={
item === "storage"
? "cloud"
: item === "fileSize"
? "file"
: "image-outline"
}
size={AppFontSize.lg}
/>
<Paragraph size={AppFontSize.sm}>
{strings[item as "storage" | "hdImages" | "fileSize"]()}
</Paragraph>
</View>
<Heading size={AppFontSize.sm}>
{
PlanOverView[plan.id as keyof typeof PlanOverView][
item as "storage" | "hdImages" | "fileSize"
]
}
</Heading>
</View>
)
)}
</View>
<Button
title={strings.selectPlan()}
type={plan.id === "pro" ? "accent" : "secondary"}
style={{
width: "100%"
}}
onPress={() => {
if (isNotReady) return;
const currentPlanSubscribed =
PremiumService.get() &&
(pricingPlans?.user?.subscription?.productId ===
(product as RNIap.Subscription)?.productId ||
pricingPlans?.user?.subscription?.productId?.startsWith(
(product as RNIap.Subscription)?.productId
));
pricingPlans?.selectPlan(
plan.id,
currentPlanSubscribed
? `notesnook.${plan.id}.${
!(product as RNIap.Subscription)?.productId.includes(
"yearly"
)
? "yearly"
: "monthly"
}`
: pricingPlans.isGithubRelease
? (WebPlan?.period as string)
: (product?.productId as string)
);
setStep(Steps.buy);
}}
/>
</View>
</View>
);
};

View File

@@ -0,0 +1,84 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useThemeColors } from "@notesnook/theme";
import React from "react";
import { Image, TouchableOpacity, View } from "react-native";
//@ts-ignore
import { openLinkInBrowser } from "../../utils/functions";
import { AppFontSize } from "../../utils/size";
import Paragraph from "../ui/typography/paragraph";
import { Radius, Spacing } from "../../common/design/spacing";
import Heading from "../ui/typography/heading";
export const ReviewItem = (props: {
review: string;
user: string;
userSource: string;
link: string;
userImage?: string;
}) => {
const { colors } = useThemeColors();
return (
<TouchableOpacity
activeOpacity={1}
onPress={() => {
openLinkInBrowser(props.link);
}}
style={{
width: "100%",
padding: Spacing.LEVEL_4,
borderWidth: 1,
backgroundColor: colors.secondary.background,
borderRadius: Radius.LG,
borderColor: colors.primary.border
}}
>
<View
style={{
flexDirection: "row",
alignItems: "center",
gap: 10,
paddingBottom: Spacing.LEVEL_4
}}
>
{props.userImage ? (
<Image
source={{
uri: props.userImage
}}
style={{
width: 34,
height: 34,
borderRadius: 100
}}
/>
) : null}
<View>
<Heading size={AppFontSize.sm}>{props.user}</Heading>
<Paragraph size={AppFontSize.xs} color={colors.primary.paragraph}>
{props.userSource}
</Paragraph>
</View>
</View>
<Paragraph>{props.review}</Paragraph>
</TouchableOpacity>
);
};

View File

@@ -23,7 +23,6 @@ import dayjs from "dayjs";
import React, { useState } from "react";
import {
Linking,
Platform,
ScrollView,
Text,
TouchableOpacity,
@@ -41,12 +40,15 @@ import { DefaultAppStyles } from "../../../utils/styles";
import { Button } from "../../ui/button";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { Radius, Spacing } from "../../../common/design/spacing";
import useGlobalSafeAreaInsets from "../../../hooks/use-global-safe-area-insets";
const isGithubRelease = Config.GITHUB_RELEASE === "true";
export const BuyPlan = (props: {
planId: string;
canActivateTrial?: boolean;
pricingPlans: ReturnType<typeof usePricingPlans>;
}) => {
const insets = useGlobalSafeAreaInsets();
const { colors } = useThemeColors();
const [checkoutUrl, setCheckoutUrl] = useState<string>();
const pricingPlans = props.pricingPlans;
@@ -63,6 +65,18 @@ export const BuyPlan = (props: {
: (pricingPlans.selectedProduct as RNIap.Product)?.productId
)?.includes("5");
const isAnnual = isGithubRelease
? (pricingPlans.selectedProduct as Plan)?.period === "yearly"
: (pricingPlans.selectedProduct as RNIap.Product)?.productId?.includes(
"yearly"
);
const hasTrialOffer = pricingPlans.hasTrialOffer(
props.planId,
(pricingPlans.selectedProduct as RNIap.Product)?.productId ||
(pricingPlans.selectedProduct as Plan)?.period
);
return checkoutUrl ? (
<View
style={{
@@ -88,162 +102,274 @@ export const BuyPlan = (props: {
/>
</View>
) : (
<ScrollView
contentContainerStyle={{
marginTop: DefaultAppStyles.GAP_VERTICAL
<View
style={{
flex: 1
}}
keyboardDismissMode="none"
keyboardShouldPersistTaps="always"
>
<View
style={{
paddingHorizontal: DefaultAppStyles.GAP,
gap: DefaultAppStyles.GAP_VERTICAL
<ScrollView
contentContainerStyle={{
paddingVertical: Spacing.LEVEL_4
}}
keyboardDismissMode="none"
keyboardShouldPersistTaps="always"
>
{[
Config.GITHUB_RELEASE === "true"
? "yearly"
: `notesnook.${props.planId}.yearly`,
Config.GITHUB_RELEASE === "true"
? "monthly"
: `notesnook.${props.planId}.monthly`,
...(props.planId === "essential" || pricingPlans.isSubscribed()
? []
: [
Config.GITHUB_RELEASE === "true"
? "5-year"
: `notesnook.${props.planId}.5year`
])
].map((item) => (
<ProductItem
key={item}
pricingPlans={pricingPlans}
productId={item}
/>
))}
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
borderWidth: 1,
borderColor: colors.primary.border,
padding: DefaultAppStyles.GAP,
borderRadius: defaultBorderRadius
paddingHorizontal: DefaultAppStyles.GAP,
gap: Spacing.LEVEL_4
}}
>
<Heading color={colors.primary.paragraph} size={AppFontSize.sm}>
{strings.dueToday()}{" "}
{pricingPlans.hasTrialOffer(
props.planId,
(pricingPlans?.selectedProduct as RNIap.Product)?.productId ||
(pricingPlans?.selectedProduct as Plan)?.period
) ? (
<Text
style={{
color: colors.primary.accent
}}
>
({strings.daysFree(`${billingDuration?.duration || 0}`)})
</Text>
) : null}
</Heading>
<Paragraph color={colors.primary.paragraph}>
{pricingPlans.hasTrialOffer(
props.planId,
(pricingPlans?.selectedProduct as RNIap.Product)?.productId ||
(pricingPlans?.selectedProduct as Plan)?.period
)
? "FREE"
: pricingPlans.getStandardPrice(
pricingPlans.selectedProduct as RNIap.Subscription
)}
</Paragraph>
</View>
{pricingPlans.hasTrialOffer(
props.planId,
(pricingPlans?.selectedProduct as RNIap.Product)?.productId ||
(pricingPlans?.selectedProduct as Plan)?.period
) ? (
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
borderWidth: 1,
borderColor: colors.primary.border,
padding: DefaultAppStyles.GAP,
borderRadius: defaultBorderRadius
gap: Spacing.LEVEL_2
}}
>
<Paragraph color={colors.secondary.paragraph}>
{strings.due(
dayjs()
.add(billingDuration?.duration || 0, "day")
.format("DD MMMM")
)}
</Paragraph>
<Paragraph color={colors.secondary.paragraph}>
{pricingPlans.getStandardPrice(
pricingPlans.selectedProduct as RNIap.Subscription
)}
</Paragraph>
</View>
) : null}
{pricingPlans.hasTrialOffer(
props.planId,
(pricingPlans.selectedProduct as RNIap.Product)?.productId ||
(pricingPlans.selectedProduct as Plan)?.period
) || is5YearPlanSelected ? (
<View
style={{
gap: DefaultAppStyles.GAP_VERTICAL,
borderWidth: 1,
borderColor: colors.primary.border,
padding: DefaultAppStyles.GAP,
borderRadius: defaultBorderRadius
}}
>
{(is5YearPlanSelected
? strings["5yearPlanConditions"]()
: [
strings.trialPlanConditions[0](
billingDuration?.duration as number as never
),
...(isGithubRelease
? []
: [strings.trialPlanConditions[1](Platform.OS as never)])
]
).map((item) => (
<View
style={{
flexDirection: "row",
alignItems: "center",
gap: 10,
flex: 1
}}
{[
Config.GITHUB_RELEASE === "true"
? "yearly"
: `notesnook.${props.planId}.yearly`,
Config.GITHUB_RELEASE === "true"
? "monthly"
: `notesnook.${props.planId}.monthly`,
...(props.planId === "essential" || pricingPlans.isSubscribed()
? []
: [
Config.GITHUB_RELEASE === "true"
? "5-year"
: `notesnook.${props.planId}.5year`
])
].map((item) => (
<ProductItem
key={item}
>
<Icon
color={colors.primary.accent}
size={AppFontSize.lg}
name="check"
/>
<Paragraph
style={{
flexShrink: 1
}}
>
{item}
</Paragraph>
</View>
pricingPlans={pricingPlans}
productId={item}
/>
))}
</View>
) : null}
<Paragraph
fontFamily="MEDIUM"
fontSize="MD"
color={colors.secondary.paragraph}
>
{strings.paymentSummary()}
</Paragraph>
<View
style={{
borderWidth: 1,
borderColor: colors.primary.border,
borderRadius: Radius.S,
backgroundColor: colors.secondary.background,
padding: Spacing.LEVEL_3
}}
>
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center"
}}
>
<View
style={{
gap: Spacing.LEVEL_1
}}
>
<Heading color={colors.primary.paragraph} size={AppFontSize.md}>
{strings.dueToday()}{" "}
</Heading>
<Paragraph>
{hasTrialOffer ? (
<Text>
{strings.freeTrialIncludes(
billingDuration?.duration || 0
)}
</Text>
) : is5YearPlanSelected ? (
strings.billingType.annual()
) : null}
</Paragraph>
</View>
<Heading color={colors.primary.paragraph} fontSize="SM">
{hasTrialOffer
? "Free"
: pricingPlans.getStandardPrice(
pricingPlans.selectedProduct as RNIap.Subscription
)}
</Heading>
</View>
{hasTrialOffer ? (
<>
<View
style={{
width: "100%",
height: 1,
backgroundColor: colors.primary.border,
marginVertical: Spacing.LEVEL_2
}}
/>
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center"
}}
>
<View
style={{
gap: Spacing.LEVEL_1
}}
>
<Heading
color={colors.primary.paragraph}
size={AppFontSize.md}
>
{strings.nextBillingDate()}
</Heading>
<Paragraph>
{dayjs()
.add(billingDuration?.duration || 0, "day")
.format("DD MMMM,YYYY")}{" "}
*{" "}
{isAnnual
? strings.billingType.annual()
: is5YearPlanSelected
? strings.billingType.oneTime()
: strings.billingType.monthly()}
</Paragraph>
</View>
<Heading fontSize="SM" color={colors.primary.accent}>
{pricingPlans.getStandardPrice(
pricingPlans.selectedProduct as RNIap.Subscription
)}
</Heading>
</View>
</>
) : null}
</View>
<Paragraph
fontFamily="MEDIUM"
fontSize="MD"
color={colors.secondary.paragraph}
>
{strings.whatsIncluded()}
</Paragraph>
<View
style={{
gap: Spacing.LEVEL_2,
borderWidth: 1,
borderColor: colors.primary.border,
padding: Spacing.LEVEL_3,
borderRadius: Radius.S,
backgroundColor: colors.secondary.background,
marginBottom: 27
}}
>
{[
strings.planWhatsIncluded.unlimitedNotes(),
strings.planWhatsIncluded.endToEnd(),
strings.planWhatsIncluded.allDevices(),
hasTrialOffer
? strings.planWhatsIncluded.freeTrial(
billingDuration?.duration || 0
)
: undefined,
hasTrialOffer ? strings.planWhatsIncluded.remind() : undefined,
...(is5YearPlanSelected
? strings["5yearPlanConditions"]()
: ([] as string[]))
].map((item) =>
!item ? null : (
<View
style={{
flexDirection: "row",
alignItems: "center",
gap: Spacing.LEVEL_1,
flex: 1
}}
key={item}
>
<Icon
color={colors.primary.accent}
size={AppFontSize.lg}
name="check"
/>
<Paragraph
style={{
flexShrink: 1
}}
>
{item}
</Paragraph>
</View>
)
)}
<Paragraph
style={{
marginTop: Spacing.LEVEL_0
}}
fontSize="XS"
>
<Heading fontSize="XS">{strings.note()}: </Heading>
{is5YearPlanSelected
? strings.oneTimePurchase()
: strings.cancelAnytimeAlt()}
</Paragraph>
</View>
<Paragraph
style={{
textAlign: "center"
}}
color={colors.secondary.paragraph}
size={AppFontSize.xs}
>
{strings.subTerms[0]()}{" "}
<Text
style={{
color: colors.primary.accent
}}
onPress={() => {
openLinkInBrowser("https://notesnook.com/privacy");
}}
>
{strings.subTerms[1]()}
</Text>{" "}
{strings.subTerms[2]()}{" "}
<Text
style={{
color: colors.primary.accent
}}
onPress={() => {
openLinkInBrowser("https://notesnook.com/tos");
}}
>
{strings.subTerms[3]()}
</Text>
</Paragraph>
</View>
</ScrollView>
<View
style={{
backgroundColor: colors.secondary.background,
width: "100%",
padding: Spacing.LEVEL_3,
marginBottom: -insets.bottom,
paddingBottom: insets.bottom,
borderTopWidth: 1,
borderTopColor: colors.primary.border
}}
>
<Button
width="100%"
type="accent"
@@ -278,50 +404,8 @@ export const BuyPlan = (props: {
);
}}
/>
<Paragraph
style={{
textAlign: "center"
}}
color={colors.secondary.paragraph}
size={AppFontSize.xs}
>
{is5YearPlanSelected
? strings.oneTimePurchase()
: strings.cancelAnytimeAlt()}
</Paragraph>
<Paragraph
style={{
textAlign: "center"
}}
color={colors.secondary.paragraph}
size={AppFontSize.xs}
>
{strings.subTerms[0]()}{" "}
<Text
style={{
textDecorationLine: "underline"
}}
onPress={() => {
openLinkInBrowser("https://notesnook.com/privacy");
}}
>
{strings.subTerms[1]()}
</Text>{" "}
{strings.subTerms[2]()}{" "}
<Text
style={{
textDecorationLine: "underline"
}}
onPress={() => {
openLinkInBrowser("https://notesnook.com/tos");
}}
>
{strings.subTerms[3]()}
</Text>
</Paragraph>
</View>
</ScrollView>
</View>
);
};
@@ -389,7 +473,13 @@ const ProductItem = (props: {
style={{
flexDirection: "row",
gap: 10,
opacity: isSubscribed ? 0.5 : 1
opacity: isSubscribed ? 0.5 : 1,
backgroundColor: colors.secondary.background,
padding: Spacing.LEVEL_2,
borderRadius: Radius.S,
borderWidth: 1,
borderColor: colors.primary.border,
justifyContent: "space-between"
}}
activeOpacity={0.9}
onPress={() => {
@@ -408,60 +498,85 @@ const ProductItem = (props: {
);
}}
>
<Icon
name={isSelected ? "radiobox-marked" : "radiobox-blank"}
color={isSelected ? colors.primary.accent : colors.secondary.icon}
size={AppFontSize.lg}
/>
<View>
<View
style={{
flexDirection: "row",
gap: Spacing.LEVEL_1
}}
>
<Icon
name={isSelected ? "radiobox-marked" : "radiobox-blank"}
color={isSelected ? colors.primary.accent : colors.secondary.icon}
size={AppFontSize.lg}
/>
<View
style={{
flexDirection: "row",
gap: DefaultAppStyles.GAP_VERTICAL_SMALL
gap: Spacing.LEVEL_1
}}
>
<Heading size={AppFontSize.md}>
{isAnnual
? strings.yearly()
: is5YearProduct
? strings.fiveYearPlan()
: strings.monthly()}
</Heading>
<View
style={{
flexDirection: "row",
gap: DefaultAppStyles.GAP_VERTICAL_SMALL
}}
>
<Heading size={AppFontSize.md}>
{isAnnual
? strings.yearly()
: is5YearProduct
? strings.fiveYearPlan()
: strings.monthly()}
</Heading>
{discountValue ? (
<View
style={{
backgroundColor: colors.static.red,
borderRadius: defaultBorderRadius,
paddingHorizontal: 6,
alignItems: "center",
justifyContent: "center"
}}
>
<Heading color={colors.static.white} size={AppFontSize.xs}>
{strings.bestValue()} - {strings.percentOff(`${discountValue}`)}
</Heading>
</View>
) : null}
{discountValue ? (
<View
style={{
backgroundColor: colors.primary.accent,
borderRadius: defaultBorderRadius,
padding: Spacing.LEVEL_0,
alignItems: "center",
justifyContent: "center"
}}
>
<Paragraph color={colors.static.white} size={AppFontSize.xxs}>
{strings.percentOff(`${discountValue}`)}
</Paragraph>
</View>
) : null}
{isSubscribed ? (
<View
style={{
backgroundColor: colors.primary.accent,
borderRadius: defaultBorderRadius,
paddingHorizontal: 6,
alignItems: "center",
justifyContent: "center"
}}
>
<Heading color={colors.static.white} size={AppFontSize.xs}>
{strings.currentPlan()}
</Heading>
</View>
) : null}
{isSubscribed ? (
<View
style={{
backgroundColor: colors.primary.accent,
borderRadius: defaultBorderRadius,
paddingHorizontal: 6,
alignItems: "center",
justifyContent: "center"
}}
>
<Heading color={colors.static.white} size={AppFontSize.xs}>
{strings.currentPlan()}
</Heading>
</View>
) : null}
</View>
<Paragraph>
{is5YearProduct
? strings.billingType.oneTime()
: isAnnual
? strings.billingType.annual()
: strings.billingType.monthly()}
</Paragraph>
</View>
</View>
<Paragraph size={AppFontSize.md}>
<View
style={{
gap: Spacing.LEVEL_1
}}
>
<Heading size={AppFontSize.sm}>
{isAnnual || is5YearProduct
? `${props.pricingPlans.getPrice(
product as RNIap.Subscription,
@@ -472,15 +587,17 @@ const ProductItem = (props: {
? 1
: 0,
isAnnual
)}/${strings.month()}`
)}`
: null}
{!isAnnual && !is5YearProduct
? `${props.pricingPlans.getStandardPrice(
product as RNIap.Subscription
)}/${strings.month()}`
)}`
: null}
</Paragraph>
</Heading>
<Paragraph size={AppFontSize.xs}>/month</Paragraph>
</View>
</TouchableOpacity>
);

View File

@@ -37,22 +37,22 @@ export const PlanOverView = {
free: {
storage: `50 MB/mo`,
fileSize: `1 MB`,
hdImages: false
hdImages: "No"
},
essential: {
storage: `1 GB`,
fileSize: `100 MB/mo`,
hdImages: false
hdImages: "No"
},
pro: {
storage: `10 GB/mo`,
fileSize: `1 GB`,
hdImages: true
hdImages: "Yes"
},
believer: {
storage: `25 GB/mo`,
fileSize: `5 GB`,
hdImages: true
hdImages: "Yes"
}
};

View File

@@ -90,7 +90,7 @@ function getSize() {
return {
xxxs: FontSizes.XXS,
xxs: FontSizes.XXS,
xs: FontSizes.XX,
xs: FontSizes.XS,
sm: FontSizes.SM,
md: FontSizes.MD,
lg: FontSizes.LG,

View File

@@ -128,7 +128,8 @@ const EXTRA_ICON_NAMES = [
"identifier",
"image-area",
"clock-outline",
"delete-sweep-outline"
"delete-sweep-outline",
"image-outline"
];
const __filename = fileURLToPath(import.meta.url);

View File

@@ -1,6 +1,6 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2026-06-16 13:03+0500\n"
"POT-Creation-Date: 2026-06-16 13:04+0500\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -502,6 +502,10 @@ msgstr "{days, plural, one {1 day} other {# days}}"
msgid "{days} days free"
msgstr "{days} days free"
#: src/strings.ts:2828
msgid "{duration} days free trial includes all features"
msgstr "{duration} days free trial includes all features"
#: src/strings.ts:351
msgid "{freq, plural, one {Repeats every day on {selectedDays} at {date}} other {Repeats every # day every {selectedDays} at {date}}}"
msgstr "{freq, plural, one {Repeats every day on {selectedDays} at {date}} other {Repeats every # day every {selectedDays} at {date}}}"
@@ -639,8 +643,8 @@ msgid "2FA code sent via {method}"
msgstr "2FA code sent via {method}"
#: src/strings.ts:2611
msgid "5 year plan (One time purchase)"
msgstr "5 year plan (One time purchase)"
msgid "5 year plan"
msgstr "5 year plan"
#: src/strings.ts:1861
msgid "6 digit code"
@@ -666,6 +670,10 @@ msgstr "Abc"
msgid "About"
msgstr "About"
#: src/strings.ts:2822
msgid "Access on all devices"
msgstr "Access on all devices"
#: src/strings.ts:1040
msgid "Account"
msgstr "Account"
@@ -1292,10 +1300,18 @@ msgstr "Beta"
msgid "Bi-directional note link"
msgstr "Bi-directional note link"
#: src/strings.ts:2813
msgid "Billed annually"
msgstr "Billed annually"
#: src/strings.ts:2581
msgid "billed annually at {price}"
msgstr "billed annually at {price}"
#: src/strings.ts:2814
msgid "Billed monthly"
msgstr "Billed monthly"
#: src/strings.ts:2582
msgid "billed monthly at {price}"
msgstr "billed monthly at {price}"
@@ -2010,8 +2026,8 @@ msgid "Create a tag to group related notes together."
msgstr "Create a tag to group related notes together."
#: src/strings.ts:1865
msgid "Create account"
msgstr "Create account"
msgid "Create Account"
msgstr "Create Account"
#: src/strings.ts:2684
msgid "Create API Key"
@@ -2108,8 +2124,8 @@ msgid "Current pin"
msgstr "Current pin"
#: src/strings.ts:1789
msgid "CURRENT PLAN"
msgstr "CURRENT PLAN"
msgid "Current Plan"
msgstr "Current Plan"
#: src/strings.ts:2026
msgid "Custom"
@@ -2716,6 +2732,10 @@ msgstr "Encryption key"
msgid "End-to-end encrypted."
msgstr "End-to-end encrypted."
#: src/strings.ts:2821
msgid "End-to-end encryption (XChaCha20)"
msgstr "End-to-end encryption (XChaCha20)"
#: src/strings.ts:399
msgid "Enter 6 digit code"
msgstr "Enter 6 digit code"
@@ -3249,6 +3269,10 @@ msgstr ""
msgid "Forgot password?"
msgstr "Forgot password?"
#: src/strings.ts:2823
msgid "Free {days} days trial, cancel anytime"
msgstr "Free {days} days trial, cancel anytime"
#: src/strings.ts:2591
msgid "Free {duration} day trial, cancel any time"
msgstr "Free {duration} day trial, cancel any time"
@@ -3257,6 +3281,10 @@ msgstr "Free {duration} day trial, cancel any time"
msgid "Free plan"
msgstr "Free plan"
#: src/strings.ts:2810
msgid "Frequently Asked Questions"
msgstr "Frequently Asked Questions"
#: src/strings.ts:627
msgid "Fri"
msgstr "Fri"
@@ -3418,8 +3446,8 @@ msgid "Having problems with sync?"
msgstr "Having problems with sync?"
#: src/strings.ts:2580
msgid "hdImages"
msgstr "hdImages"
msgid "HD Images"
msgstr "HD Images"
#: src/strings.ts:2376
msgid "Heading {level}"
@@ -4414,6 +4442,10 @@ msgstr "Newly created notes will be uncategorized"
msgid "Next"
msgstr "Next"
#: src/strings.ts:2826
msgid "Next Billing Date"
msgstr "Next Billing Date"
#: src/strings.ts:2405
msgid "Next match"
msgstr "Next match"
@@ -4719,6 +4751,10 @@ msgstr "Oldest - newest"
msgid "Once your password is changed, please make sure to save the new account recovery key"
msgstr "Once your password is changed, please make sure to save the new account recovery key"
#: src/strings.ts:2815
msgid "One time purchase"
msgstr "One time purchase"
#: src/strings.ts:2587
msgid "One time purchase, no auto-renewal"
msgstr "One time purchase, no auto-renewal"
@@ -4904,6 +4940,10 @@ msgstr "Pay once and use for 5 years"
msgid "Payment method"
msgstr "Payment method"
#: src/strings.ts:2818
msgid "Payment summary"
msgstr "Payment summary"
#: src/strings.ts:788
msgid "PDF is password protected"
msgstr "PDF is password protected"
@@ -5529,6 +5569,10 @@ msgstr "Relogin to your account"
msgid "Remembered your password?"
msgstr "Remembered your password?"
#: src/strings.ts:2824
msgid "Remind before your trial ends"
msgstr "Remind before your trial ends"
#: src/strings.ts:941
msgid "Remind me"
msgstr "Remind me"
@@ -6137,6 +6181,10 @@ msgstr "Select notes to link to \"{title}\""
msgid "Select nth day of the month to repeat the reminder."
msgstr "Select nth day of the month to repeat the reminder."
#: src/strings.ts:2809
msgid "Select Plan"
msgstr "Select Plan"
#: src/strings.ts:1833
msgid "Select profile picture"
msgstr "Select profile picture"
@@ -6822,6 +6870,10 @@ msgstr "Test connection"
msgid "Test connection before changing server urls"
msgstr "Test connection before changing server urls"
#: src/strings.ts:2811
msgid "Testimonials"
msgstr "Testimonials"
#: src/strings.ts:2309
msgid "Text color"
msgstr "Text color"
@@ -7118,8 +7170,8 @@ msgid "Turns off syncing completely on this device. Any changes made will remain
msgstr "Turns off syncing completely on this device. Any changes made will remain local only and new changes from your other devices won't sync to this device."
#: src/strings.ts:110
msgid "Two factor authentication"
msgstr "Two factor authentication"
msgid "Two Factor Authentication"
msgstr "Two Factor Authentication"
#: src/strings.ts:494
msgid "Two-factor authentication"
@@ -7165,6 +7217,10 @@ msgstr "Unfavorite"
msgid "Unlimited"
msgstr "Unlimited"
#: src/strings.ts:2820
msgid "Unlimited notes and attachments"
msgstr "Unlimited notes and attachments"
#: src/strings.ts:946
msgid "Unlink from all"
msgstr "Unlink from all"
@@ -7664,6 +7720,10 @@ msgstr "What is your refund policy?"
msgid "What went wrong?"
msgstr "What went wrong?"
#: src/strings.ts:2817
msgid "What's included"
msgstr "What's included"
#: src/strings.ts:2536
msgid "Why do you need my credit card details for a free trial?"
msgstr "Why do you need my credit card details for a free trial?"

View File

@@ -1,6 +1,6 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2026-06-16 13:03+0500\n"
"POT-Creation-Date: 2026-06-16 13:04+0500\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -502,6 +502,10 @@ msgstr ""
msgid "{days} days free"
msgstr ""
#: src/strings.ts:2828
msgid "{duration} days free trial includes all features"
msgstr ""
#: src/strings.ts:351
msgid "{freq, plural, one {Repeats every day on {selectedDays} at {date}} other {Repeats every # day every {selectedDays} at {date}}}"
msgstr ""
@@ -639,7 +643,7 @@ msgid "2FA code sent via {method}"
msgstr ""
#: src/strings.ts:2611
msgid "5 year plan (One time purchase)"
msgid "5 year plan"
msgstr ""
#: src/strings.ts:1861
@@ -666,6 +670,10 @@ msgstr ""
msgid "About"
msgstr ""
#: src/strings.ts:2822
msgid "Access on all devices"
msgstr ""
#: src/strings.ts:1040
msgid "Account"
msgstr ""
@@ -1292,10 +1300,18 @@ msgstr ""
msgid "Bi-directional note link"
msgstr ""
#: src/strings.ts:2813
msgid "Billed annually"
msgstr ""
#: src/strings.ts:2581
msgid "billed annually at {price}"
msgstr ""
#: src/strings.ts:2814
msgid "Billed monthly"
msgstr ""
#: src/strings.ts:2582
msgid "billed monthly at {price}"
msgstr ""
@@ -1999,7 +2015,7 @@ msgid "Create a tag to group related notes together."
msgstr ""
#: src/strings.ts:1865
msgid "Create account"
msgid "Create Account"
msgstr ""
#: src/strings.ts:2684
@@ -2097,7 +2113,7 @@ msgid "Current pin"
msgstr ""
#: src/strings.ts:1789
msgid "CURRENT PLAN"
msgid "Current Plan"
msgstr ""
#: src/strings.ts:2026
@@ -2705,6 +2721,10 @@ msgstr ""
msgid "End-to-end encrypted."
msgstr ""
#: src/strings.ts:2821
msgid "End-to-end encryption (XChaCha20)"
msgstr ""
#: src/strings.ts:399
msgid "Enter 6 digit code"
msgstr ""
@@ -3231,6 +3251,10 @@ msgstr ""
msgid "Forgot password?"
msgstr ""
#: src/strings.ts:2823
msgid "Free {days} days trial, cancel anytime"
msgstr ""
#: src/strings.ts:2591
msgid "Free {duration} day trial, cancel any time"
msgstr ""
@@ -3239,6 +3263,10 @@ msgstr ""
msgid "Free plan"
msgstr ""
#: src/strings.ts:2810
msgid "Frequently Asked Questions"
msgstr ""
#: src/strings.ts:627
msgid "Fri"
msgstr ""
@@ -3400,7 +3428,7 @@ msgid "Having problems with sync?"
msgstr ""
#: src/strings.ts:2580
msgid "hdImages"
msgid "HD Images"
msgstr ""
#: src/strings.ts:2376
@@ -4394,6 +4422,10 @@ msgstr ""
msgid "Next"
msgstr ""
#: src/strings.ts:2826
msgid "Next Billing Date"
msgstr ""
#: src/strings.ts:2405
msgid "Next match"
msgstr ""
@@ -4693,6 +4725,10 @@ msgstr ""
msgid "Once your password is changed, please make sure to save the new account recovery key"
msgstr ""
#: src/strings.ts:2815
msgid "One time purchase"
msgstr ""
#: src/strings.ts:2587
msgid "One time purchase, no auto-renewal"
msgstr ""
@@ -4878,6 +4914,10 @@ msgstr ""
msgid "Payment method"
msgstr ""
#: src/strings.ts:2818
msgid "Payment summary"
msgstr ""
#: src/strings.ts:788
msgid "PDF is password protected"
msgstr ""
@@ -5503,6 +5543,10 @@ msgstr ""
msgid "Remembered your password?"
msgstr ""
#: src/strings.ts:2824
msgid "Remind before your trial ends"
msgstr ""
#: src/strings.ts:941
msgid "Remind me"
msgstr ""
@@ -6111,6 +6155,10 @@ msgstr ""
msgid "Select nth day of the month to repeat the reminder."
msgstr ""
#: src/strings.ts:2809
msgid "Select Plan"
msgstr ""
#: src/strings.ts:1833
msgid "Select profile picture"
msgstr ""
@@ -6781,6 +6829,10 @@ msgstr ""
msgid "Test connection before changing server urls"
msgstr ""
#: src/strings.ts:2811
msgid "Testimonials"
msgstr ""
#: src/strings.ts:2309
msgid "Text color"
msgstr ""
@@ -7077,7 +7129,7 @@ msgid "Turns off syncing completely on this device. Any changes made will remain
msgstr ""
#: src/strings.ts:110
msgid "Two factor authentication"
msgid "Two Factor Authentication"
msgstr ""
#: src/strings.ts:494
@@ -7124,6 +7176,10 @@ msgstr ""
msgid "Unlimited"
msgstr ""
#: src/strings.ts:2820
msgid "Unlimited notes and attachments"
msgstr ""
#: src/strings.ts:946
msgid "Unlink from all"
msgstr ""
@@ -7614,6 +7670,10 @@ msgstr ""
msgid "What went wrong?"
msgstr ""
#: src/strings.ts:2817
msgid "What's included"
msgstr ""
#: src/strings.ts:2536
msgid "Why do you need my credit card details for a free trial?"
msgstr ""

View File

@@ -107,7 +107,7 @@ export const strings = {
},
alreadyHaveAccount: () => t`Already have an account?`,
login: () => t`Login`,
"2fa": () => t`Two factor authentication`,
"2fa": () => t`Two Factor Authentication`,
select2faMethod: () => t`Select method for two-factor authentication`,
select2faCodeHelpText: () => t`Select how you would like to recieve the code`,
"2faCodeHelpText": {
@@ -1786,7 +1786,7 @@ For example:
dragAndDropFiles: () => t`Drag & drop files here, or click to select files`,
onlyZipSupported: () => t`Only .zip files are supported.`,
clickToRemove: () => t`Click to remove`,
currentPlan: () => t`CURRENT PLAN`,
currentPlan: () => t`Current Plan`,
appWillReloadIn: (sec: number) => t`App will reload in ${sec} seconds`,
changesReflectOnStart: () =>
t`Your changes have been saved and will be reflected after the app has refreshed.`,
@@ -1862,7 +1862,7 @@ For example:
newEmail: () => t`New Email`,
accountPassword: () => t`Account password`,
phoneNumber: () => t`Phone number`,
createAccount: () => t`Create account`,
createAccount: () => t`Create Account`,
accountRecoverHelpText: () =>
t`You will receive instructions on how to recover your account on this email`,
enterRecoveryKey: () => t`Enter account recovery key`,
@@ -2577,7 +2577,7 @@ Use this if changes from other devices are not appearing on this device. This wi
thankYouForSubscribing: () => t`Thank you for subscribing`,
settingUpPlan: () =>
t`Were setting up your plan right now. Well notify you as soon as everything is ready.`,
hdImages: () => t`hdImages`,
hdImages: () => t`HD Images`,
billedAnnually: (price: string) => t`billed annually at ${price}`,
billedMonthly: (price: string) => t`billed monthly at ${price}`,
dueToday: () => t`Due today`,
@@ -2608,7 +2608,7 @@ Use this if changes from other devices are not appearing on this device. This wi
bestValue: () => t`Best value`,
planLimits: () => t`Plan limits`,
unlimited: () => t`Unlimited`,
fiveYearPlan: () => t`5 year plan (One time purchase)`,
fiveYearPlan: () => t`5 year plan`,
educationPlan: () => t`Education plan`,
welcomeToPlan: (plan: string) => t`Welcome to Notesnook ${plan}`,
thankYouForPurchase: () => t`Thank you for the purchase`,
@@ -2805,5 +2805,25 @@ Continue without attachments?`,
setupInboxKeys: () => t`Setup inbox keys`,
enterPgpPublicKey: () => t`Enter your PGP public key`,
enterPgpPrivateKey: () => t`Enter your PGP private key`,
expiryDateRemoved: () => t`Expiry date removed`
expiryDateRemoved: () => t`Expiry date removed`,
selectPlan: () => t`Select Plan`,
frequentlyAskedQuestions: () => t`Frequently Asked Questions`,
testimonials: () => t`Testimonials`,
billingType: {
annual: () => t`Billed annually`,
monthly: () => t`Billed monthly`,
oneTime: () => t`One time purchase`
},
whatsIncluded: () => t`What's included`,
paymentSummary: () => t`Payment summary`,
planWhatsIncluded: {
unlimitedNotes: () => t`Unlimited notes and attachments`,
endToEnd: () => t`End-to-end encryption (XChaCha20)`,
allDevices: () => t`Access on all devices`,
freeTrial: (days: number) => t`Free ${days} days trial, cancel anytime`,
remind: () => t`Remind before your trial ends`
},
nextBillingDate: () => t`Next Billing Date`,
freeTrialIncludes: (duration: number) =>
t`${duration} days free trial includes all features`
};