130 lines
6.1 KiB
TypeScript
130 lines
6.1 KiB
TypeScript
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>
|
||
);
|
||
} |