Skip to content

Instantly share code, notes, and snippets.

@pylixonly
Last active April 1, 2025 09:40
Show Gist options
  • Save pylixonly/a47bd70a362258c0fc8cd06dcee8b08f to your computer and use it in GitHub Desktop.
Save pylixonly/a47bd70a362258c0fc8cd06dcee8b08f to your computer and use it in GitHub Desktop.
Unpublished code for Bunny Premium™️ upselling
import { hideSheet, showSheet } from "@lib/ui/sheets";
import { createStyles } from "@lib/ui/styles";
import { NavigationNative, tokens } from "@metro/common";
import { ActionSheet, Button, PressableScale, Text } from "@metro/common/components";
import { debounce } from "es-toolkit";
import { useEffect, useLayoutEffect, useState } from "react";
import { ActivityIndicator, ScrollView, TouchableOpacity, View } from "react-native";
const showPaymentError = debounce(() => {
showSheet(
"bunny-payment-sheet",
() => {
const [loading, setLoading] = useState(true);
useEffect(() => {
setTimeout(() => {
setLoading(false);
}, 2000);
}, []);
if (loading) {
return <ActionSheet>
<ActivityIndicator />
</ActionSheet>;
}
return <ActionSheet>
<View style={{ padding: 8, gap: 8 }}>
<Text variant="heading-lg/bold">Payment Error</Text>
<Text variant="text-md/medium" style={{ marginBottom: 16 }}>
Your payment could not be processed. Please try again later.
</Text>
<Button
size="lg"
text="Close"
onPress={() => hideSheet("bunny-payment-sheet")}
/>
</View>
</ActionSheet>;
}
);
}, 50);
const PremiumScreen = () => {
const styles = useStyles();
const navigation = NavigationNative.useNavigation();
const [selectedPlan, setSelectedPlan] = useState<null | "basic" | "pro" | "ultimate">(null);
useLayoutEffect(() => {
navigation.setOptions({
headerShown: false,
});
}, [navigation]);
const renderFeature = (icon: any, text: any) => (
<View style={styles.featureRow}>
<Text style={styles.featureIcon}>{icon}</Text>
<Text variant="text-md/medium">{text}</Text>
</View>
);
return (
<ScrollView style={styles.container}>
<TouchableOpacity
style={styles.backButton}
onPress={() => navigation.goBack()}
>
<Text variant="eyebrow">← Back</Text>
</TouchableOpacity>
<View
style={styles.header}
>
<Text style={{ textAlign: "center" }} variant="display-md">Bunny Premium</Text>
<Text style={{ textAlign: "center" }} variant="text-md/medium">
Starting tomorrow, the Bunny client mod will switch to a paid subscription model. Without an active premium subscription, Bunny will stop working on this device. Below are the available subscription tiers:
</Text>
</View>
<View style={styles.content}>
<View style={styles.planContainer}>
<PressableScale
style={[styles.planCard, selectedPlan === "basic" && styles.selectedPlan]}
onPress={() => setSelectedPlan("basic")}
>
<Text variant="heading-md/bold">Bunny Basic</Text>
<Text variant="heading-xl/bold">$4.99/month</Text>
<Text variant="text-md/medium" color="text-muted" style={styles.planDescription}>
Basic features with minor inconveniences
</Text>
{renderFeature("🫷", "Unskippable ads every 10 minutes")}
{renderFeature("🔌", "Install up to 5 plugins")}
{renderFeature("💬", "Cooldown 1 message/hour")}
</PressableScale>
<PressableScale
style={[styles.planCard, selectedPlan === "pro" && styles.selectedPlan]}
onPress={() => setSelectedPlan("pro")}
>
<View style={styles.popularTag}>
<Text variant="eyebrow" color="white">POPULAR</Text>
</View>
<Text variant="heading-md/bold">Bunny Pro</Text>
<Text variant="heading-xl/bold">$9.99/month</Text>
<Text variant="text-md/medium" color="text-muted" style={styles.planDescription}>Premium upgrades for power users</Text>
{renderFeature("⏩", "Skip ads after 5 seconds")}
{renderFeature("🔌", "Access up to 10 plugins")}
{renderFeature("💬", "Cooldown 2 messages/min")}
</PressableScale>
<PressableScale
style={[styles.planCard, selectedPlan === "ultimate" && styles.selectedPlan]}
onPress={() => setSelectedPlan("ultimate")}
>
<Text variant="heading-md/bold">Bunny Ultimate</Text>
<Text variant="heading-xl/bold">$19.99/month</Text>
<Text variant="text-md/medium" color="text-muted" style={styles.planDescription}>Legendary Bunny experience</Text>
{renderFeature("📺", "No ads, but your data are still tracked")}
{renderFeature("🔌", "Unlimited plugins")}
{renderFeature("💬", "Cooldown 5 messages/min")}
</PressableScale>
</View>
<Button
grow={true}
size="lg"
style={{ marginBottom: 24 }}
text={selectedPlan ? "Unlock Premium Features" : "Select a Plan"}
onPress={showPaymentError}
disabled={!selectedPlan}
/>
<Text variant="text-xs/normal" color="text-muted">
Terms and conditions apply, including but not limited to: quantum entanglement of your data,
Schrödinger's plugin cap (it exists and doesn't exist simultaneously), and the occasional invocation
of the Bunnyverse's interdimensional licensing agreement. Proceed at your own risk.
</Text>
</View>
</ScrollView>
);
};
const useStyles = createStyles(() => ({
backButton: {
position: "absolute",
top: 16,
left: 16,
zIndex: 1,
},
container: {
flex: 1,
},
header: {
gap: 8,
paddingBottom: 8,
paddingTop: 64,
paddingHorizontal: 16,
alignItems: "center",
},
content: {
padding: 16,
},
planContainer: {
marginBottom: 24,
},
planCard: {
backgroundColor: tokens.colors.CARD_PRIMARY_BG,
borderRadius: 12,
padding: 16,
marginBottom: 16,
borderWidth: 2,
borderColor: "transparent",
},
selectedPlan: {
borderColor: tokens.colors.BG_BRAND,
backgroundColor: tokens.colors.CARD_SECONDARY_BG,
},
popularTag: {
position: "absolute",
right: 0,
top: 0,
backgroundColor: tokens.colors.BG_BRAND,
paddingHorizontal: 8,
paddingVertical: 4,
borderTopRightRadius: 10,
borderBottomLeftRadius: 10,
},
planDescription: {
marginBottom: 16,
fontStyle: "italic",
},
featureRow: {
flexDirection: "row",
alignItems: "center",
marginBottom: 8,
},
featureIcon: {
height: 16,
fontSize: 12,
marginRight: 8,
}
}));
export default PremiumScreen;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment