238 lines
7.2 KiB
TypeScript
238 lines
7.2 KiB
TypeScript
import React, { useEffect, useRef, useState } from "react";
|
|
import {
|
|
View,
|
|
Text,
|
|
ScrollView,
|
|
Image,
|
|
TouchableOpacity,
|
|
Share,
|
|
} from "react-native";
|
|
import { router } from "expo-router";
|
|
import ScreenWrapper from "~/components/ui/ScreenWrapper";
|
|
import BackButton from "~/components/ui/backButton";
|
|
import { Button } from "~/components/ui/button";
|
|
import { Icons } from "~/assets/icons";
|
|
import { ROUTES } from "~/lib/routes";
|
|
import { useTranslation } from "react-i18next";
|
|
import { getPointsState, type PointsState } from "~/lib/services/pointsService";
|
|
import * as Clipboard from "expo-clipboard";
|
|
import ModalToast from "~/components/ui/toast";
|
|
|
|
// Placeholder referral link
|
|
const REFERRAL_LINK = "ambapay.com/frars";
|
|
|
|
export default function PointsScreen() {
|
|
const { t } = useTranslation();
|
|
const [pointsState, setPointsState] = useState<PointsState | null>(null);
|
|
const [toastVisible, setToastVisible] = useState(false);
|
|
const [toastTitle, setToastTitle] = useState("");
|
|
const [toastDescription, setToastDescription] = useState<string | undefined>(
|
|
undefined
|
|
);
|
|
const [toastVariant, setToastVariant] = useState<
|
|
"success" | "error" | "warning" | "info"
|
|
>("info");
|
|
const toastTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
(async () => {
|
|
try {
|
|
const state = await getPointsState();
|
|
if (!cancelled) {
|
|
setPointsState(state);
|
|
}
|
|
} catch (error) {
|
|
if (__DEV__) {
|
|
console.warn("[PointsScreen] Failed to load points state", error);
|
|
}
|
|
}
|
|
})();
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, []);
|
|
const showToast = (
|
|
title: string,
|
|
description?: string,
|
|
variant: "success" | "error" | "warning" | "info" = "info"
|
|
) => {
|
|
if (toastTimeoutRef.current) {
|
|
clearTimeout(toastTimeoutRef.current);
|
|
}
|
|
|
|
setToastTitle(title);
|
|
setToastDescription(description);
|
|
setToastVariant(variant);
|
|
setToastVisible(true);
|
|
|
|
toastTimeoutRef.current = setTimeout(() => {
|
|
setToastVisible(false);
|
|
toastTimeoutRef.current = null;
|
|
}, 2500);
|
|
};
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
if (toastTimeoutRef.current) {
|
|
clearTimeout(toastTimeoutRef.current);
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
const handleCopyLink = async () => {
|
|
try {
|
|
await Clipboard.setStringAsync(REFERRAL_LINK);
|
|
showToast("Copied", "Referral link copied to clipboard", "success");
|
|
} catch (error) {
|
|
showToast("Error", "Failed to copy referral link", "error");
|
|
}
|
|
};
|
|
|
|
const handleShare = async () => {
|
|
try {
|
|
await Share.share({
|
|
message: `Use my Amba referral link: ${REFERRAL_LINK}`,
|
|
});
|
|
} catch (error) {
|
|
// Optional: only show error toast if share actually fails (not cancelled)
|
|
showToast("Error", "Failed to open share options", "error");
|
|
}
|
|
};
|
|
|
|
const handleActivityPress = () => {
|
|
router.push(ROUTES.POINTS_ACTIVITY);
|
|
};
|
|
|
|
const handleHowToEarnPress = () => {
|
|
// Navigate to how-to-earn details when available
|
|
};
|
|
|
|
const handleRedeem = (rewardId: string) => {
|
|
// Integrate redeem API later
|
|
};
|
|
|
|
return (
|
|
<ScreenWrapper edges={["bottom"]}>
|
|
<BackButton />
|
|
|
|
<ScrollView contentContainerStyle={{ paddingBottom: 32 }}>
|
|
<View className="px-5 pt-4 space-y-6">
|
|
<Text className="text-3xl font-dmsans-bold text-black">
|
|
{t("points.title")}
|
|
</Text>
|
|
|
|
{/* Total Points Card */}
|
|
<View className="mt-4 bg-[#E9FFF4] rounded-[8px] px-5 py-4 flex-row items-center justify-between">
|
|
<View>
|
|
<Text className="text-sm font-dmsans text-gray-500 mb-1">
|
|
Your Points
|
|
</Text>
|
|
<Text className="text-3xl font-dmsans-bold text-[#0F7B4A]">
|
|
{pointsState?.total ?? 0}
|
|
</Text>
|
|
</View>
|
|
<Image
|
|
source={Icons.coinIcon}
|
|
style={{ width: 36, height: 36 }}
|
|
resizeMode="contain"
|
|
/>
|
|
</View>
|
|
|
|
<View className="space-y-4 pt-6">
|
|
<Text className="text-xl font-dmsans-medium text-black">
|
|
{t("points.referTitle")}
|
|
</Text>
|
|
<Text className="text-xl font-dmsans-medium text-black">
|
|
{t("points.earnSubtitle")}
|
|
</Text>
|
|
</View>
|
|
|
|
<View className="bg-[#E9FFF4] rounded-[6px] p-6 mt-8 space-y-3">
|
|
<View className="flex-row items-center justify-between bg-[#FFB668] rounded-[4px] px-4 py-4">
|
|
<Text
|
|
className="text-sm font-dmsans-medium text-black"
|
|
numberOfLines={1}
|
|
>
|
|
{REFERRAL_LINK}
|
|
</Text>
|
|
<TouchableOpacity
|
|
onPress={handleCopyLink}
|
|
className="flex-row items-center space-x-2"
|
|
>
|
|
<Text className="text-md font-dmsans mr-2 text-black">
|
|
{t("points.copyButton")}
|
|
</Text>
|
|
<Image
|
|
source={Icons.copyIcon}
|
|
style={{ width: 17, height: 17 }}
|
|
resizeMode="contain"
|
|
/>
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
<Button
|
|
className="mt-3 bg-[#0F7B4A] rounded-[4px] h-11"
|
|
onPress={handleShare}
|
|
>
|
|
<Text className="text-white font-dmsans-bold text-base">
|
|
{t("points.shareButton")}
|
|
</Text>
|
|
</Button>
|
|
</View>
|
|
|
|
<View className="flex-row mt-6 space-x-6 gap-6">
|
|
<TouchableOpacity
|
|
onPress={handleActivityPress}
|
|
className="flex-1 bg-[#0F7B4A] rounded-[4px] px-5 py-4 flex-row items-center justify-center space-x-3"
|
|
>
|
|
<Image
|
|
source={Icons.activityIcon}
|
|
style={{
|
|
width: 22,
|
|
height: 22,
|
|
tintColor: "#FFB668",
|
|
marginRight: 4,
|
|
}}
|
|
resizeMode="contain"
|
|
/>
|
|
<Text className="text-white font-dmsans-bold text-base">
|
|
{t("points.activityButton")}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
|
|
<TouchableOpacity
|
|
onPress={handleHowToEarnPress}
|
|
className="flex-1 bg-[#0F7B4A] rounded-[4px] px-5 py-4 flex-row items-center justify-center space-x-3"
|
|
>
|
|
<Image
|
|
source={Icons.moneyIcon}
|
|
style={{
|
|
width: 22,
|
|
height: 22,
|
|
tintColor: "#FFB668",
|
|
marginRight: 4,
|
|
}}
|
|
resizeMode="contain"
|
|
/>
|
|
<Text className="text-white font-dmsans-bold text-base">
|
|
{t("points.howToEarnButton")}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
{/* Rewards section can be wired to real redemption in the future */}
|
|
</View>
|
|
</ScrollView>
|
|
|
|
<ModalToast
|
|
visible={toastVisible}
|
|
title={toastTitle}
|
|
description={toastDescription}
|
|
variant={toastVariant}
|
|
/>
|
|
</ScreenWrapper>
|
|
);
|
|
}
|