mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-07-09 20:09:36 +02:00
mobile: update ui for paywall flow
This commit is contained in:
File diff suppressed because one or more lines are too long
24
apps/mobile/app/components/paywall/common.ts
Normal file
24
apps/mobile/app/components/paywall/common.ts
Normal 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
|
||||
};
|
||||
181
apps/mobile/app/components/paywall/compare-plans.tsx
Normal file
181
apps/mobile/app/components/paywall/compare-plans.tsx
Normal 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";
|
||||
73
apps/mobile/app/components/paywall/faq-item.tsx
Normal file
73
apps/mobile/app/components/paywall/faq-item.tsx
Normal 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
332
apps/mobile/app/components/paywall/plan-card.tsx
Normal file
332
apps/mobile/app/components/paywall/plan-card.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
84
apps/mobile/app/components/paywall/review-item.tsx
Normal file
84
apps/mobile/app/components/paywall/review-item.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Binary file not shown.
@@ -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);
|
||||
|
||||
@@ -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?"
|
||||
|
||||
@@ -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 ""
|
||||
|
||||
@@ -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`We’re setting up your plan right now. We’ll 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`
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user