diff --git a/App.tsx b/App.tsx index 113549eb..102d672c 100644 --- a/App.tsx +++ b/App.tsx @@ -43,7 +43,6 @@ import { aiService } from './src/services/aiService'; import { AccountProvider, useAccount } from './src/contexts/AccountContext'; import { ToastProvider } from './src/contexts/ToastContext'; import { mmkvStorage } from './src/services/mmkvStorage'; -import AnnouncementOverlay from './src/components/AnnouncementOverlay'; import { CampaignManager } from './src/components/promotions/CampaignManager'; Sentry.init({ @@ -91,7 +90,6 @@ const ThemedApp = () => { const { currentTheme } = useTheme(); const [isAppReady, setIsAppReady] = useState(false); const [hasCompletedOnboarding, setHasCompletedOnboarding] = useState(null); - const [showAnnouncement, setShowAnnouncement] = useState(false); // Update popup functionality const { @@ -106,16 +104,6 @@ const ThemedApp = () => { // GitHub major/minor release overlay const githubUpdate = useGithubMajorUpdate(); - // Announcement data - const announcements = [ - { - icon: 'zap', - title: 'Debrid Integration', - description: 'Unlock 4K high-quality streams with lightning-fast speeds. Connect your TorBox account to access cached premium content with zero buffering.', - tag: 'NEW', - }, - ]; - // Check onboarding status and initialize services useEffect(() => { const initializeApp = async () => { @@ -135,15 +123,6 @@ const ThemedApp = () => { await aiService.initialize(); console.log('AI service initialized'); - // Check if announcement should be shown (version 1.0.0) - const announcementShown = await mmkvStorage.getItem('announcement_v1.0.0_shown'); - if (!announcementShown && onboardingCompleted === 'true') { - // Show announcement only after app is ready - setTimeout(() => { - setShowAnnouncement(true); - }, 1000); - } - } catch (error) { console.error('Error initializing app:', error); // Default to showing onboarding if we can't check @@ -181,20 +160,6 @@ const ThemedApp = () => { // Navigation reference const navigationRef = React.useRef(null); - // Handler for navigating to debrid integration - const handleNavigateToDebrid = () => { - if (navigationRef.current) { - navigationRef.current.navigate('DebridIntegration'); - } - }; - - // Handler for announcement close - const handleAnnouncementClose = async () => { - setShowAnnouncement(false); - // Mark announcement as shown - await mmkvStorage.setItem('announcement_v1.0.0_shown', 'true'); - }; - // Don't render anything until we know the onboarding status const shouldShowApp = isAppReady && hasCompletedOnboarding !== null; const initialRouteName = hasCompletedOnboarding ? 'MainTabs' : 'Onboarding'; @@ -237,13 +202,6 @@ const ThemedApp = () => { onDismiss={githubUpdate.onDismiss} onLater={githubUpdate.onLater} /> - diff --git a/src/components/player/overlays/ParentalGuideOverlay.tsx b/src/components/player/overlays/ParentalGuideOverlay.tsx index d88335c6..583402e3 100644 --- a/src/components/player/overlays/ParentalGuideOverlay.tsx +++ b/src/components/player/overlays/ParentalGuideOverlay.tsx @@ -74,6 +74,7 @@ export const ParentalGuideOverlay: React.FC = ({ const hasShownRef = useRef(false); const hideTimeoutRef = useRef(null); const fadeTimeoutRef = useRef(null); + const prevShouldShowRef = useRef(false); // Animation values const lineHeight = useSharedValue(0); @@ -130,9 +131,51 @@ export const ParentalGuideOverlay: React.FC = ({ fetchData(); }, [imdbId, type, season, episode]); - // Trigger animation when shouldShow becomes true + // Handle show/hide based on shouldShow (controls visibility) useEffect(() => { - if (shouldShow && warnings.length > 0 && !hasShownRef.current) { + // When controls are shown (shouldShow becomes false), immediately hide overlay + if (!shouldShow && isVisible) { + // Clear any pending timeouts + if (hideTimeoutRef.current) { + clearTimeout(hideTimeoutRef.current); + hideTimeoutRef.current = null; + } + if (fadeTimeoutRef.current) { + clearTimeout(fadeTimeoutRef.current); + fadeTimeoutRef.current = null; + } + + // Immediately hide overlay with quick fade out + const count = warnings.length; + // FADE OUT: Items fade out in reverse order (bottom to top) + for (let i = count - 1; i >= 0; i--) { + const reverseDelay = (count - 1 - i) * 40; + itemOpacities[i].value = withDelay( + reverseDelay, + withTiming(0, { duration: 100 }) + ); + } + + // Line shrinks after items are gone + const lineDelay = count * 40 + 50; + lineHeight.value = withDelay(lineDelay, withTiming(0, { + duration: 200, + easing: Easing.in(Easing.cubic), + })); + + // Container fades out last + containerOpacity.value = withDelay(lineDelay + 100, withTiming(0, { duration: 150 })); + + // Set invisible after all animations complete + fadeTimeoutRef.current = setTimeout(() => { + setIsVisible(false); + // Don't reset hasShownRef here - only reset on content change + }, lineDelay + 300); + } + + // When controls are hidden (shouldShow becomes true), show overlay if not already shown for this content + // Only show if transitioning from false to true (controls just hidden) + if (shouldShow && !prevShouldShowRef.current && warnings.length > 0 && !hasShownRef.current) { hasShownRef.current = true; setIsVisible(true); @@ -182,10 +225,14 @@ export const ParentalGuideOverlay: React.FC = ({ // Set invisible after all animations complete fadeTimeoutRef.current = setTimeout(() => { setIsVisible(false); + // Don't reset hasShownRef - only reset on content change }, lineDelay + 500); }, 5000); } - }, [shouldShow, warnings.length]); + + // Update previous shouldShow value + prevShouldShowRef.current = shouldShow; + }, [shouldShow, isVisible, warnings.length]); // Cleanup on unmount useEffect(() => { @@ -198,6 +245,7 @@ export const ParentalGuideOverlay: React.FC = ({ // Reset when content changes useEffect(() => { hasShownRef.current = false; + prevShouldShowRef.current = false; setWarnings([]); setIsVisible(false); lineHeight.value = 0; diff --git a/src/screens/SettingsScreen.tsx b/src/screens/SettingsScreen.tsx index 1a491b14..69db5212 100644 --- a/src/screens/SettingsScreen.tsx +++ b/src/screens/SettingsScreen.tsx @@ -432,21 +432,6 @@ const SettingsScreen: React.FC = () => { renderControl={() => } isTablet={isTablet} /> - { - try { - await mmkvStorage.removeItem('announcement_v1.0.0_shown'); - openAlert('Success', 'Announcement reset. Restart the app to see the announcement overlay.'); - } catch (error) { - openAlert('Error', 'Failed to reset announcement.'); - } - }} - renderControl={() => } - isTablet={isTablet} - /> { } }; - const handleResetAnnouncement = async () => { - try { - await mmkvStorage.removeItem('announcement_v1.0.0_shown'); - openAlert('Success', 'Announcement reset. Restart the app to see the announcement overlay.'); - } catch (error) { - openAlert('Error', 'Failed to reset announcement.'); - } - }; - const handleResetCampaigns = async () => { await campaignService.resetCampaigns(); openAlert('Success', 'Campaign history reset. Restart app to see posters again.'); @@ -127,13 +118,6 @@ const DeveloperSettingsScreen: React.FC = () => { onPress={handleResetOnboarding} renderControl={() => } /> - } - />