added push notifications

This commit is contained in:
2026-06-03 19:13:56 +02:00
parent a9278b8269
commit 6559a9cd4b
24 changed files with 1598 additions and 557 deletions
+8 -1
View File
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, {useCallback, useState} from "react";
import {
View, Text, ScrollView, TouchableOpacity,
ActivityIndicator, useColorScheme,
@@ -7,6 +7,7 @@ import { SafeAreaView } from "react-native-safe-area-context";
import { Ionicons } from "@expo/vector-icons";
import SearchBar from "../components/SearchBar";
import CollapsibleCategory from "../components/CollapsibleCategory";
import {useFocusEffect} from "expo-router";
const GROUPS = [
@@ -40,6 +41,12 @@ export default function AllFeed() {
setTimeout(() => setRefreshing(false), 800);
};
useFocusEffect(
useCallback(() => {
handleRefresh();
}, [])
);
return (
<SafeAreaView className={`flex-1 ${dark ? "bg-obsidian-200" : "bg-parchment-50"}`}>
{/* Header */}
+30 -19
View File
@@ -23,6 +23,8 @@ type SettingRowProps = {
accent?: string;
};
function SettingRow({ icon, label, value, toggle, toggleValue, onToggle, onPress, accent }: SettingRowProps) {
const dark = useColorScheme() === "dark";
const iconColor = accent ?? (dark ? "#706D67" : "#8A8278");
@@ -149,11 +151,25 @@ function GuestProfile({ onSignIn }: { onSignIn: () => void }) {
function AuthenticatedProfile({ onSignOut }: { onSignOut: () => void }) {
const [showSubs, setShowSubs] = useState(false);
const dark = useColorScheme() === "dark";
const { user } = useAuth();
const [notifications, setNotifications] = React.useState(true);
const { user, updateNotificationType } = useAuth();
const [notifications, setNotifications] = useState(
user?.notificationType === 'PUSH_NOTIFICATION'
);
const [digest, setDigest] = React.useState(false);
const [showEdit, setShowEdit] = useState(false);
const [showHelp, setShowHelp] = useState(false);
const handleNotificationToggle = async (value: boolean) => {
setNotifications(value);
try {
await updateNotificationType(
value ? 'PUSH_NOTIFICATION' : 'NO_NOTIFICATION'
);
} catch {
setNotifications(!value); // revert on failure
}
};
return (
<>
<ScrollView contentContainerStyle={{ paddingBottom: 48 }} showsVerticalScrollIndicator={false}>
@@ -197,13 +213,19 @@ function AuthenticatedProfile({ onSignOut }: { onSignOut: () => void }) {
>
<SectionLabel title="Notifications" />
<SettingRow
icon="notifications-outline" label="Push notifications (TODO)"
toggle toggleValue={notifications} onToggle={setNotifications}
icon="notifications-outline"
label="Push notifications"
toggle
toggleValue={notifications}
onToggle={handleNotificationToggle}
accent={dark ? "#E07B45" : "#C4622D"}
/>
<SettingRow
icon="mail-outline" label="Daily digest email (TODO)"
toggle toggleValue={digest} onToggle={setDigest}
icon="mail-outline"
label="Weekly Digest email (TODO)"
toggle
toggleValue={digest}
onToggle={setDigest}
accent={dark ? "#5E9EF4" : "#3B7DD8"}
/>
@@ -231,32 +253,21 @@ function AuthenticatedProfile({ onSignOut }: { onSignOut: () => void }) {
<Text className="text-sm font-sans font-semibold text-red-500">Sign out</Text>
</View>
</TouchableOpacity>
</ScrollView>
<Modal visible={showEdit} animationType="slide" presentationStyle="pageSheet" onRequestClose={() => setShowEdit(false)}>
<EditProfile onClose={() => setShowEdit(false)} />
</Modal>
<Modal visible={showHelp} animationType="slide" presentationStyle="pageSheet" onRequestClose={() => setShowHelp(false)}>
<HelpSupport onClose={() => setShowHelp(false)} />
</Modal>
<Modal
visible={showSubs}
animationType="slide"
presentationStyle="pageSheet"
onRequestClose={() => setShowSubs(false)}
>
<Modal visible={showSubs} animationType="slide" presentationStyle="pageSheet" onRequestClose={() => setShowSubs(false)}>
<ManageSubscriptions onClose={() => setShowSubs(false)} />
</Modal>
</>
);
}
export default function Profile() {
const dark = useColorScheme() === "dark";
const { user, logout } = useAuth();
+9 -3
View File
@@ -9,6 +9,7 @@ import SearchBar from "../components/SearchBar";
import ExpandableItem from "../components/ExpandableItem";
import { useAuth } from "../context/AuthContext";
import { Entry, entriesApi } from "../services/api";
import {useFocusEffect} from "expo-router";
type SortMode = "time" | "subject";
@@ -124,9 +125,14 @@ export default function SubscribedFeed() {
} finally {
setLoading(false);
}
}, [user?.subscribedSubjectIds]);
}, [user?.subscribedSubjectIds?.join(',')]);
useEffect(() => { fetchFeed(); }, [fetchFeed]);
// Always re-fetch when tab gains focus
useFocusEffect(
useCallback(() => {
fetchFeed();
}, [fetchFeed])
);
// Filter then group
const filtered = useMemo(() => {
@@ -249,7 +255,7 @@ export default function SubscribedFeed() {
<View className="items-center mt-16 px-8">
<Text className="text-4xl mb-3">🔍</Text>
<Text className={`text-base font-sans font-semibold text-center ${dark ? "text-ink-dark" : "text-ink-light"}`}>
No results for "{query}"
No results for &#34;{query}&#34;
</Text>
</View>
) : (