etfoglasi-frontend/screens/ManageSubscriptions.tsx
2026-05-12 21:33:41 +02:00

130 lines
6.1 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useEffect, useState } from "react";
import {
View, Text, ScrollView, TouchableOpacity,
ActivityIndicator, useColorScheme,
} from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { Ionicons } from "@expo/vector-icons";
import { Subject, subjectsApi } from "../services/api";
import { useAuth } from "../context/AuthContext";
export default function ManageSubscriptions({ onClose }: { onClose: () => void }) {
const dark = useColorScheme() === "dark";
const { user, isSubscribed, subscribe, unsubscribe } = useAuth();
const accent = dark ? "#E07B45" : "#C4622D";
const [subjects, setSubjects] = useState<Subject[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
const [toggling, setToggling] = useState<string | null>(null);
useEffect(() => {
subjectsApi.getAll(user?.token)
.then(setSubjects)
.catch(() => setError("Couldn't load subjects."))
.finally(() => setLoading(false));
}, []);
const toggle = async (subject: Subject) => {
setToggling(subject.id);
try {
if (isSubscribed(subject.id)) await unsubscribe(subject.id);
else await subscribe(subject.id);
} finally {
setToggling(null);
}
};
return (
<SafeAreaView className={`flex-1 ${dark ? "bg-obsidian-200" : "bg-parchment-50"}`}>
{/* Header */}
<View className={`flex-row items-center px-4 py-4 border-b ${
dark ? "border-border-dark" : "border-border-light"
}`}>
<Text className={`flex-1 text-xl font-display font-bold ${dark ? "text-ink-dark" : "text-ink-light"}`}>
Manage Subscriptions
</Text>
<TouchableOpacity
onPress={onClose}
className={`w-8 h-8 rounded-full items-center justify-center ${dark ? "bg-obsidian-50" : "bg-parchment-200"}`}
>
<Ionicons name="close" size={18} color={dark ? "#706D67" : "#8A8278"} />
</TouchableOpacity>
</View>
{/* Body */}
{loading ? (
<View className="flex-1 items-center justify-center">
<ActivityIndicator color={accent} />
</View>
) : error ? (
<View className="flex-1 items-center justify-center px-8">
<Text className="text-3xl mb-3"></Text>
<Text className={`text-sm font-sans text-center ${dark ? "text-muted-dark" : "text-muted-light"}`}>
{error}
</Text>
</View>
) : (
<ScrollView
contentContainerStyle={{ padding: 16, paddingBottom: 40 }}
showsVerticalScrollIndicator={false}
>
<View className={`rounded-2xl overflow-hidden border ${
dark ? "bg-obsidian-100 border-border-dark" : "bg-surface-light border-border-light"
}`}>
{subjects.map((subject, index) => {
const subscribed = isSubscribed(subject.id);
const busy = toggling === subject.id;
const isLast = index === subjects.length - 1;
return (
<View
key={subject.id}
className={`flex-row items-center px-4 py-3.5 ${
!isLast ? `border-b ${dark ? "border-border-dark" : "border-border-light"}` : ""
}`}
>
{/* Subscribed indicator dot */}
<View
className="w-2 h-2 rounded-full mr-3"
style={{
backgroundColor: subscribed ? accent : "transparent",
borderWidth: subscribed ? 0 : 1.5,
borderColor: dark ? "#706D67" : "#8A8278",
}}
/>
<Text className={`flex-1 text-sm font-sans ${dark ? "text-ink-dark" : "text-ink-light"}`}>
{subject.name}
</Text>
<TouchableOpacity
onPress={() => toggle(subject)}
disabled={!!toggling}
className="px-3 py-1.5 rounded-xl"
style={{
backgroundColor: subscribed
? dark ? "#2C2A27" : "#F0EDE8"
: accent + "20",
}}
activeOpacity={0.7}
>
{busy ? (
<ActivityIndicator size="small" color={accent} style={{ width: 40 }} />
) : (
<Text className="text-xs font-sans font-semibold" style={{ color: accent }}>
{subscribed ? "Unsubscribe" : "Subscribe"}
</Text>
)}
</TouchableOpacity>
</View>
);
})}
</View>
</ScrollView>
)}
</SafeAreaView>
);
}