diff --git a/src/components/home/ContentItem.tsx b/src/components/home/ContentItem.tsx
index f4ff1bc..4ad0774 100644
--- a/src/components/home/ContentItem.tsx
+++ b/src/components/home/ContentItem.tsx
@@ -59,6 +59,17 @@ const POSTER_WIDTH = posterLayout.posterWidth;
const PLACEHOLDER_BLURHASH = 'LEHV6nWB2yk8pyo0adR*.7kCMdnj';
const ContentItem = ({ item, onPress, shouldLoadImage: shouldLoadImageProp, deferMs = 0 }: ContentItemProps) => {
+ // Track inLibrary status locally to force re-render
+ const [inLibrary, setInLibrary] = useState(!!item.inLibrary);
+
+ useEffect(() => {
+ // Subscribe to library updates and update local state if this item's status changes
+ const unsubscribe = catalogService.subscribeToLibraryUpdates((items) => {
+ const found = items.find((libItem) => libItem.id === item.id && libItem.type === item.type);
+ setInLibrary(!!found);
+ });
+ return () => unsubscribe();
+ }, [item.id, item.type]);
const [menuVisible, setMenuVisible] = useState(false);
const [isWatched, setIsWatched] = useState(false);
const [imageLoaded, setImageLoaded] = useState(false);
@@ -95,7 +106,7 @@ const ContentItem = ({ item, onPress, shouldLoadImage: shouldLoadImageProp, defe
const handleOptionSelect = useCallback((option: string) => {
switch (option) {
case 'library':
- if (item.inLibrary) {
+ if (inLibrary) {
catalogService.removeFromLibrary(item.type, item.id);
} else {
catalogService.addToLibrary(item);
@@ -109,7 +120,7 @@ const ContentItem = ({ item, onPress, shouldLoadImage: shouldLoadImageProp, defe
case 'share':
break;
}
- }, [item]);
+ }, [item, inLibrary]);
const handleMenuClose = useCallback(() => {
setMenuVisible(false);
@@ -238,7 +249,7 @@ const ContentItem = ({ item, onPress, shouldLoadImage: shouldLoadImageProp, defe
)}
{imageError && (
-
+
)}
@@ -247,7 +258,7 @@ const ContentItem = ({ item, onPress, shouldLoadImage: shouldLoadImageProp, defe
)}
- {item.inLibrary && (
+ {inLibrary && (
@@ -266,6 +277,8 @@ const ContentItem = ({ item, onPress, shouldLoadImage: shouldLoadImageProp, defe
onClose={handleMenuClose}
item={item}
onOptionSelect={handleOptionSelect}
+ isSaved={inLibrary}
+ isWatched={isWatched}
/>
>
);
diff --git a/src/components/home/DropUpMenu.tsx b/src/components/home/DropUpMenu.tsx
index 7431655..d84ca62 100644
--- a/src/components/home/DropUpMenu.tsx
+++ b/src/components/home/DropUpMenu.tsx
@@ -33,9 +33,11 @@ interface DropUpMenuProps {
onClose: () => void;
item: StreamingContent;
onOptionSelect: (option: string) => void;
+ isSaved?: boolean; // allow parent to pass saved status directly
+ isWatched?: boolean; // allow parent to pass watched status directly
}
-export const DropUpMenu = ({ visible, onClose, item, onOptionSelect }: DropUpMenuProps) => {
+export const DropUpMenu = ({ visible, onClose, item, onOptionSelect, isSaved: isSavedProp, isWatched: isWatchedProp }: DropUpMenuProps) => {
const translateY = useSharedValue(300);
const opacity = useSharedValue(0);
const isDarkMode = useColorScheme() === 'dark';
@@ -87,15 +89,18 @@ export const DropUpMenu = ({ visible, onClose, item, onOptionSelect }: DropUpMen
borderTopRightRadius: 24,
}));
+ // Robustly determine if the item is in the library (saved)
+ const isSaved = typeof isSavedProp === 'boolean' ? isSavedProp : !!item.inLibrary;
+ const isWatched = !!isWatchedProp;
const menuOptions = [
{
- icon: item.inLibrary ? 'bookmark' : 'bookmark-border',
- label: item.inLibrary ? 'Remove from Library' : 'Add to Library',
+ icon: isSaved ? 'bookmark' : 'bookmark-border',
+ label: isSaved ? 'Remove from Library' : 'Add to Library',
action: 'library'
},
{
icon: 'check-circle',
- label: 'Mark as Watched',
+ label: isWatched ? 'Mark as Unwatched' : 'Mark as Watched',
action: 'watched'
},
{
diff --git a/src/screens/NotificationSettingsScreen.tsx b/src/screens/NotificationSettingsScreen.tsx
index 4b45e80..2708c55 100644
--- a/src/screens/NotificationSettingsScreen.tsx
+++ b/src/screens/NotificationSettingsScreen.tsx
@@ -116,7 +116,7 @@ const NotificationSettingsScreen = () => {
const resetAllNotifications = async () => {
Alert.alert(
'Reset Notifications',
- 'This will cancel all scheduled notifications. Are you sure?',
+ 'This will cancel all scheduled notifications, but will not remove anything from your saved library. Are you sure?',
[
{
text: 'Cancel',
@@ -127,7 +127,11 @@ const NotificationSettingsScreen = () => {
style: 'destructive',
onPress: async () => {
try {
- await notificationService.cancelAllNotifications();
+ // Cancel all notifications for all series, but do not remove from saved
+ const scheduledNotifications = notificationService.getScheduledNotifications?.() || [];
+ for (const notification of scheduledNotifications) {
+ await notificationService.cancelNotification(notification.id);
+ }
Alert.alert('Success', 'All notifications have been reset');
} catch (error) {
logger.error('Error resetting notifications:', error);
@@ -164,9 +168,24 @@ const NotificationSettingsScreen = () => {
const handleTestNotification = async () => {
try {
- // Cancel previous test notification if exists
- if (testNotificationId) {
- await notificationService.cancelNotification(testNotificationId);
+ // Remove all previous test notifications before scheduling a new one
+ const scheduled = notificationService.getScheduledNotifications?.() || [];
+ const testNotifications = scheduled.filter(n => n.id.startsWith('test-notification-'));
+ if (testNotifications.length > 0 && typeof notificationService.cancelNotification === 'function') {
+ for (const n of testNotifications) {
+ await notificationService.cancelNotification(n.id);
+ }
+ }
+
+
+ // Temporarily override timeBeforeAiring to 0 for the test notification
+ let originalTimeBeforeAiring: number | undefined = undefined;
+ if (typeof notificationService.getSettings === 'function') {
+ const currentSettings = await notificationService.getSettings();
+ originalTimeBeforeAiring = currentSettings.timeBeforeAiring;
+ if (typeof notificationService.updateSettings === 'function') {
+ await notificationService.updateSettings({ timeBeforeAiring: 0 });
+ }
}
const testNotification = {
@@ -176,15 +195,24 @@ const NotificationSettingsScreen = () => {
episodeTitle: 'Test Episode',
season: 1,
episode: 1,
- releaseDate: new Date(Date.now() + 60000).toISOString(), // 1 minute from now
+ releaseDate: new Date(Date.now() + 5000).toISOString(), // 5 seconds from now
notified: false
};
-
+
const notificationId = await notificationService.scheduleEpisodeNotification(testNotification);
+
+ // Restore original timeBeforeAiring
+ if (
+ typeof notificationService.updateSettings === 'function' &&
+ originalTimeBeforeAiring !== undefined
+ ) {
+ await notificationService.updateSettings({ timeBeforeAiring: originalTimeBeforeAiring });
+ }
+
if (notificationId) {
setTestNotificationId(notificationId);
- setCountdown(60); // Start 60 second countdown
- Alert.alert('Success', 'Test notification scheduled for 1 minute from now');
+ setCountdown(0); // No countdown for instant notification
+ Alert.alert('Success', 'Test notification scheduled to fire instantly');
} else {
Alert.alert('Error', 'Failed to schedule test notification. Make sure notifications are enabled.');
}
@@ -425,7 +453,7 @@ const NotificationSettingsScreen = () => {
{countdown !== null
? `Notification in ${countdown}s...`
- : 'Test Notification (1min)'}
+ : 'Test Notification (5 sec)'}
@@ -442,10 +470,6 @@ const NotificationSettingsScreen = () => {
)}
-
-
- This will cancel all scheduled notifications. You'll need to re-enable them manually.
-
>
)}
diff --git a/src/services/catalogService.ts b/src/services/catalogService.ts
index 6b5a81c..3988513 100644
--- a/src/services/catalogService.ts
+++ b/src/services/catalogService.ts
@@ -1,4 +1,5 @@
import { stremioService, Meta, Manifest } from './stremioService';
+import { notificationService } from './notificationService';
import AsyncStorage from '@react-native-async-storage/async-storage';
import axios from 'axios';
import { TMDBService } from './tmdbService';
@@ -690,15 +691,14 @@ class CatalogService {
try { this.libraryAddListeners.forEach(l => l(content)); } catch {}
// Auto-setup notifications for series when added to library
- // if (content.type === 'series') {
- // try {
- // const { notificationService } = await import('./notificationService');
- // await notificationService.updateNotificationsForSeries(content.id);
- // console.log(`[CatalogService] Auto-setup notifications for series: ${content.name}`);
- // } catch (error) {
- // console.error(`[CatalogService] Failed to setup notifications for ${content.name}:`, error);
- // }
- // }
+ if (content.type === 'series') {
+ try {
+ await notificationService.updateNotificationsForSeries(content.id);
+ console.log(`[CatalogService] Auto-setup notifications for series: ${content.name}`);
+ } catch (error) {
+ console.error(`[CatalogService] Failed to setup notifications for ${content.name}:`, error);
+ }
+ }
}
public async removeFromLibrary(type: string, id: string): Promise {
@@ -707,24 +707,21 @@ class CatalogService {
this.saveLibrary();
this.notifyLibrarySubscribers();
try { this.libraryRemoveListeners.forEach(l => l(type, id)); } catch {}
-
+
// Cancel notifications for series when removed from library
- // if (type === 'series') {
- // try {
- // const { notificationService } = await import('./notificationService');
- // // Cancel all notifications for this series
- // const scheduledNotifications = await notificationService.getScheduledNotifications();
- // const seriesToCancel = scheduledNotifications.filter(notification => notification.seriesId === id);
- //
- // for (const notification of seriesToCancel) {
- // await notificationService.cancelNotification(notification.id);
- // }
- //
- // console.log(`[CatalogService] Cancelled ${seriesToCancel.length} notifications for removed series: ${id}`);
- // } catch (error) {
- // console.error(`[CatalogService] Failed to cancel notifications for removed series ${id}:`, error);
- // }
- // }
+ if (type === 'series') {
+ try {
+ // Cancel all notifications for this series
+ const scheduledNotifications = notificationService.getScheduledNotifications();
+ const seriesToCancel = scheduledNotifications.filter(notification => notification.seriesId === id);
+ for (const notification of seriesToCancel) {
+ await notificationService.cancelNotification(notification.id);
+ }
+ console.log(`[CatalogService] Cancelled ${seriesToCancel.length} notifications for removed series: ${id}`);
+ } catch (error) {
+ console.error(`[CatalogService] Failed to cancel notifications for removed series ${id}:`, error);
+ }
+ }
}
private addToRecentContent(content: StreamingContent): void {