mobile: allow users to change plan

This commit is contained in:
Ammar Ahmed
2025-09-24 10:23:00 +05:00
committed by Abdullah Atta
parent a49bc92a13
commit fb7d8b0893
8 changed files with 134 additions and 33 deletions

View File

@@ -63,7 +63,7 @@ import usePricingPlans, {
import Navigation, { NavigationProps } from "../../services/navigation"; import Navigation, { NavigationProps } from "../../services/navigation";
import { getElevationStyle } from "../../utils/elevation"; import { getElevationStyle } from "../../utils/elevation";
import { openLinkInBrowser } from "../../utils/functions"; import { openLinkInBrowser } from "../../utils/functions";
import { AppFontSize } from "../../utils/size"; import { AppFontSize, defaultBorderRadius } from "../../utils/size";
import { DefaultAppStyles } from "../../utils/styles"; import { DefaultAppStyles } from "../../utils/styles";
import { AuthMode } from "../auth/common"; import { AuthMode } from "../auth/common";
import { Header } from "../header"; import { Header } from "../header";
@@ -236,7 +236,6 @@ const PayWall = (props: NavigationProps<"PayWall">) => {
gap: 5 gap: 5
}} }}
> >
{/* <Icon name="crown" size={30} color={colors.static.orange} /> */}
<Heading <Heading
key="heading" key="heading"
size={AppFontSize.xl} size={AppFontSize.xl}
@@ -244,10 +243,17 @@ const PayWall = (props: NavigationProps<"PayWall">) => {
alignSelf: "center" alignSelf: "center"
}} }}
> >
{strings.notesnookPlans[0]()}{" "} {pricingPlans.isSubscribed()
<Heading size={AppFontSize.xl} color={colors.primary.accent}> ? strings.changePlan()
{strings.notesnookPlans[1]()} : strings.notesnookPlans[0]() + " "}
</Heading> {pricingPlans.isSubscribed() ? null : (
<Heading
size={AppFontSize.xl}
color={colors.primary.accent}
>
{strings.notesnookPlans[1]()}
</Heading>
)}
</Heading> </Heading>
</View> </View>
@@ -562,7 +568,7 @@ After trying all the privacy security oriented note taking apps, for the price a
<View <View
style={{ style={{
alignSelf: "center", alignSelf: isTablet ? "center" : "flex-start",
flexShrink: 1 flexShrink: 1
}} }}
> >
@@ -748,7 +754,7 @@ const ComparePlans = React.memo(
<ScrollView <ScrollView
horizontal horizontal
style={{ style={{
width: "100%" width: isTablet ? "100%" : undefined
}} }}
contentContainerStyle={{ contentContainerStyle={{
flexDirection: "column" flexDirection: "column"
@@ -988,6 +994,11 @@ const PricingPlanCard = ({
}); });
}, [pricingPlans, plan, product, WebPlan]); }, [pricingPlans, plan, product, WebPlan]);
console.log(
pricingPlans?.user?.subscription.productId,
pricingPlans?.currentPlan?.id
);
return ( return (
<TouchableOpacity <TouchableOpacity
activeOpacity={0.8} activeOpacity={0.8}
@@ -1014,7 +1025,7 @@ const PricingPlanCard = ({
<View <View
style={{ style={{
backgroundColor: colors.static.red, backgroundColor: colors.static.red,
borderRadius: 100, borderRadius: defaultBorderRadius,
paddingHorizontal: 6, paddingHorizontal: 6,
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
@@ -1028,6 +1039,26 @@ const PricingPlanCard = ({
</View> </View>
) : null} ) : null}
{product?.productId &&
pricingPlans?.user?.subscription.productId.includes(plan.id) &&
pricingPlans.isSubscribed() ? (
<View
style={{
backgroundColor: colors.primary.accent,
borderRadius: defaultBorderRadius,
paddingHorizontal: 6,
alignItems: "center",
justifyContent: "center",
height: 25,
alignSelf: "flex-start"
}}
>
<Heading color={colors.static.white} size={AppFontSize.xs}>
{strings.currentPlan()}
</Heading>
</View>
) : null}
<View> <View>
<Heading size={AppFontSize.md}> <Heading size={AppFontSize.md}>
{plan.name}{" "} {plan.name}{" "}

View File

@@ -114,7 +114,7 @@ export const BuyPlan = (props: {
Config.GITHUB_RELEASE === "true" Config.GITHUB_RELEASE === "true"
? "monthly" ? "monthly"
: `notesnook.${props.planId}.monthly`, : `notesnook.${props.planId}.monthly`,
...(props.planId === "essential" ...(props.planId === "essential" || pricingPlans.isSubscribed()
? [] ? []
: [ : [
Config.GITHUB_RELEASE === "true" Config.GITHUB_RELEASE === "true"
@@ -349,6 +349,11 @@ const ProductItem = (props: {
: (product as RNIap.Product)?.productId : (product as RNIap.Product)?.productId
)?.includes("5"); )?.includes("5");
const isSubscribed =
props.pricingPlans.isSubscribed() &&
(props.pricingPlans.user?.subscription.productId ===
(product as RNIap.Subscription).productId ||
props.pricingPlans.user?.subscription.productId === (product as Plan).id);
return ( return (
<TouchableOpacity <TouchableOpacity
style={{ style={{
@@ -390,7 +395,7 @@ const ProductItem = (props: {
<View <View
style={{ style={{
backgroundColor: colors.static.red, backgroundColor: colors.static.red,
borderRadius: 100, borderRadius: defaultBorderRadius,
paddingHorizontal: 6, paddingHorizontal: 6,
alignItems: "center", alignItems: "center",
justifyContent: "center" justifyContent: "center"
@@ -410,6 +415,22 @@ const ProductItem = (props: {
</Heading> </Heading>
</View> </View>
) : null} ) : 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> </View>
<Paragraph size={AppFontSize.md}> <Paragraph size={AppFontSize.md}>

View File

@@ -16,7 +16,12 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { Plan, SKUResponse, SubscriptionPlanId } from "@notesnook/core"; import {
Plan,
SKUResponse,
SubscriptionPlan,
SubscriptionPlanId
} from "@notesnook/core";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Platform } from "react-native"; import { Platform } from "react-native";
import Config from "react-native-config"; import Config from "react-native-config";
@@ -776,7 +781,8 @@ const usePricingPlans = (options?: PricingPlansOptions) => {
}, },
regionalDiscount, regionalDiscount,
getRegionalDiscount, getRegionalDiscount,
isGithubRelease: isGithubRelease isGithubRelease: isGithubRelease,
isSubscribed: () => user?.subscription.plan !== SubscriptionPlan.FREE
}; };
}; };

View File

@@ -18,7 +18,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { formatBytes } from "@notesnook/common"; import { formatBytes } from "@notesnook/common";
import { SubscriptionPlan, SubscriptionStatus } from "@notesnook/core"; import {
SubscriptionPlan,
SubscriptionProvider,
SubscriptionStatus
} from "@notesnook/core";
import { strings } from "@notesnook/intl"; import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme"; import { useThemeColors } from "@notesnook/theme";
import { useNetInfo } from "@react-native-community/netinfo"; import { useNetInfo } from "@react-native-community/netinfo";
@@ -34,7 +38,7 @@ import AppIcon from "../../components/ui/AppIcon";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
import { TimeSince } from "../../components/ui/time-since"; import { TimeSince } from "../../components/ui/time-since";
import Paragraph from "../../components/ui/typography/paragraph"; import Paragraph from "../../components/ui/typography/paragraph";
import { presentSheet } from "../../services/event-manager"; import { presentSheet, ToastManager } from "../../services/event-manager";
import Navigation from "../../services/navigation"; import Navigation from "../../services/navigation";
import { useThemeStore } from "../../stores/use-theme-store"; import { useThemeStore } from "../../stores/use-theme-store";
import { SyncStatus, useUserStore } from "../../stores/use-user-store"; import { SyncStatus, useUserStore } from "../../stores/use-user-store";
@@ -364,22 +368,42 @@ const SettingsUserSection = ({ item }) => {
</Paragraph> </Paragraph>
</TouchableOpacity> </TouchableOpacity>
<Button {user.subscription.provider === SubscriptionProvider.PADDLE ||
title={strings.upgradePlan()} user.subscription.provider ===
onPress={() => { SubscriptionProvider.STREETWRITERS ? null : (
Navigation.navigate("PayWall", { <Button
context: "logged-in", title={
canGoBack: true user.subscription.plan !== SubscriptionPlan.FREE
}); ? strings.changePlan()
}} : strings.upgradePlan()
type="accent" }
fontSize={AppFontSize.xs} onPress={() => {
style={{ // if (
paddingHorizontal: DefaultAppStyles.GAP_SMALL, // user.subscription.plan !== SubscriptionPlan.FREE &&
height: "auto", // user.subscription.productId.includes("5year")
paddingVertical: DefaultAppStyles.GAP_SMALL // ) {
}} // ToastManager.show({
/> // message:
// "You have made a one time purchase. To change your plan please contact support.",
// type: "info"
// });
// return;
// }
Navigation.navigate("PayWall", {
context: "logged-in",
canGoBack: true
});
}}
type="accent"
fontSize={AppFontSize.xs}
style={{
paddingHorizontal: DefaultAppStyles.GAP_SMALL,
height: "auto",
paddingVertical: DefaultAppStyles.GAP_SMALL
}}
/>
)}
</View> </View>
</View> </View>
</View> </View>

View File

@@ -111,7 +111,7 @@ export interface RouteParams extends ParamListBase {
Intro: GenericRouteParam; Intro: GenericRouteParam;
PayWall: { PayWall: {
canGoBack?: boolean; canGoBack?: boolean;
context: "signup" | "logged-in" | "logged-out"; context: "signup" | "logged-in" | "logged-out" | "subscribed";
state?: BillingState; state?: BillingState;
}; };
} }

View File

@@ -1362,6 +1362,10 @@ msgstr "Change notification sound"
msgid "Change password" msgid "Change password"
msgstr "Change password" msgstr "Change password"
#: src/strings.ts:2584
msgid "Change plan"
msgstr "Change plan"
#: src/strings.ts:1817 #: src/strings.ts:1817
msgid "Change profile picture" msgid "Change profile picture"
msgstr "Change profile picture" msgstr "Change profile picture"
@@ -7234,6 +7238,10 @@ msgstr "You have been logged out from all other devices."
msgid "You have been logged out." msgid "You have been logged out."
msgstr "You have been logged out." msgstr "You have been logged out."
#: src/strings.ts:2586
msgid "You have made a one time purchase. To change your plan please contact support."
msgstr "You have made a one time purchase. To change your plan please contact support."
#: src/strings.ts:963 #: src/strings.ts:963
msgid "You have not added any notebooks yet" msgid "You have not added any notebooks yet"
msgstr "You have not added any notebooks yet" msgstr "You have not added any notebooks yet"

View File

@@ -1362,6 +1362,10 @@ msgstr ""
msgid "Change password" msgid "Change password"
msgstr "" msgstr ""
#: src/strings.ts:2584
msgid "Change plan"
msgstr ""
#: src/strings.ts:1817 #: src/strings.ts:1817
msgid "Change profile picture" msgid "Change profile picture"
msgstr "" msgstr ""
@@ -7180,6 +7184,10 @@ msgstr ""
msgid "You have been logged out." msgid "You have been logged out."
msgstr "" msgstr ""
#: src/strings.ts:2586
msgid "You have made a one time purchase. To change your plan please contact support."
msgstr ""
#: src/strings.ts:963 #: src/strings.ts:963
msgid "You have not added any notebooks yet" msgid "You have not added any notebooks yet"
msgstr "" msgstr ""

View File

@@ -2580,5 +2580,8 @@ Use this if changes from other devices are not appearing on this device. This wi
fiveYearPlan: () => t`5 year plan (One time purchase)`, fiveYearPlan: () => t`5 year plan (One time purchase)`,
educationPlan: () => t`Education plan`, educationPlan: () => t`Education plan`,
welcomeToPlan: (plan: string) => t`Welcome to Notesnook ${plan}`, welcomeToPlan: (plan: string) => t`Welcome to Notesnook ${plan}`,
thankYouForPurchase: () => t`Thank you for the purchase` thankYouForPurchase: () => t`Thank you for the purchase`,
changePlan: () => t`Change plan`,
contactSupportToChangePlan: () =>
t`You have made a one time purchase. To change your plan please contact support.`
}; };