Amba-Agent-App/app/(root)/(screens)/points.tsx
2026-01-16 00:22:35 +03:00

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>
);
}