mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-01-11 20:10:25 +00:00
new toast library
This commit is contained in:
parent
56654e1ced
commit
16c460cdc2
12 changed files with 251 additions and 84 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -56,3 +56,4 @@ src/screens/xavio.md
|
|||
/KSPlayer
|
||||
/exobase
|
||||
ffmpegreadme.md
|
||||
toast.md
|
||||
|
|
|
|||
104
package-lock.json
generated
104
package-lock.json
generated
|
|
@ -72,9 +72,11 @@
|
|||
"react-native-screens": "~4.4.0",
|
||||
"react-native-svg": "15.8.0",
|
||||
"react-native-url-polyfill": "^2.0.0",
|
||||
"react-native-vector-icons": "^10.3.0",
|
||||
"react-native-video": "^6.12.0",
|
||||
"react-native-web": "~0.19.13",
|
||||
"react-native-wheel-color-picker": "^1.3.1"
|
||||
"react-native-wheel-color-picker": "^1.3.1",
|
||||
"toastify-react-native": "^7.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
|
|
@ -12965,6 +12967,93 @@
|
|||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-vector-icons": {
|
||||
"version": "10.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-10.3.0.tgz",
|
||||
"integrity": "sha512-IFQ0RE57819hOUdFvgK4FowM5aMXg7C7XKsuGLevqXkkIJatc3QopN0wYrb2IrzUgmdpfP+QVIbI3S6h7M0btw==",
|
||||
"deprecated": "react-native-vector-icons package has moved to a new model of per-icon-family packages. See the https://github.com/oblador/react-native-vector-icons/blob/master/MIGRATION.md on how to migrate",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.7.2",
|
||||
"yargs": "^16.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"fa-upgrade.sh": "bin/fa-upgrade.sh",
|
||||
"fa5-upgrade": "bin/fa5-upgrade.sh",
|
||||
"fa6-upgrade": "bin/fa6-upgrade.sh",
|
||||
"generate-icon": "bin/generate-icon.js"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-vector-icons/node_modules/cliui": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
||||
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-vector-icons/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-native-vector-icons/node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-vector-icons/node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-vector-icons/node_modules/yargs": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
|
||||
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cliui": "^7.0.2",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.0",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^20.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-vector-icons/node_modules/yargs-parser": {
|
||||
"version": "20.2.9",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
|
||||
"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-video": {
|
||||
"version": "6.16.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-video/-/react-native-video-6.16.1.tgz",
|
||||
|
|
@ -14853,6 +14942,19 @@
|
|||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/toastify-react-native": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/toastify-react-native/-/toastify-react-native-7.2.3.tgz",
|
||||
"integrity": "sha512-ngmpTKlTo0IRddwSsNWK+YKbB2veqotHy7Zpil4eksoLAlq0RPSgdVOk5QDEDUONJQ4r7ljGYeRW68KBztirsg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-native-vector-icons": "*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/toidentifier": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||
|
|
|
|||
|
|
@ -72,9 +72,11 @@
|
|||
"react-native-screens": "~4.4.0",
|
||||
"react-native-svg": "15.8.0",
|
||||
"react-native-url-polyfill": "^2.0.0",
|
||||
"react-native-vector-icons": "^10.3.0",
|
||||
"react-native-video": "^6.12.0",
|
||||
"react-native-web": "~0.19.13",
|
||||
"react-native-wheel-color-picker": "^1.3.1"
|
||||
"react-native-wheel-color-picker": "^1.3.1",
|
||||
"toastify-react-native": "^7.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { toast } from '@backpackapp-io/react-native-toast';
|
||||
import { Toast } from 'toastify-react-native';
|
||||
import { DeviceEventEmitter } from 'react-native';
|
||||
import { View, TouchableOpacity, ActivityIndicator, StyleSheet, Dimensions, Platform, Text, Animated, Share } from 'react-native';
|
||||
import { Image as ExpoImage } from 'expo-image';
|
||||
|
|
@ -9,6 +9,8 @@ import { useSettings } from '../../hooks/useSettings';
|
|||
import { catalogService, StreamingContent } from '../../services/catalogService';
|
||||
import { DropUpMenu } from './DropUpMenu';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { storageService } from '../../services/storageService';
|
||||
import { TraktService } from '../../services/traktService';
|
||||
|
||||
interface ContentItemProps {
|
||||
item: StreamingContent;
|
||||
|
|
@ -116,28 +118,52 @@ const ContentItem = ({ item, onPress, shouldLoadImage: shouldLoadImageProp, defe
|
|||
onPress(item.id, item.type);
|
||||
}, [item.id, item.type, onPress]);
|
||||
|
||||
const handleOptionSelect = useCallback((option: string) => {
|
||||
const handleOptionSelect = useCallback(async (option: string) => {
|
||||
switch (option) {
|
||||
case 'library':
|
||||
if (inLibrary) {
|
||||
catalogService.removeFromLibrary(item.type, item.id);
|
||||
toast('Removed from Library', { duration: 1200 });
|
||||
Toast.info('Removed from Library');
|
||||
} else {
|
||||
catalogService.addToLibrary(item);
|
||||
toast('Added to Library', { duration: 1200 });
|
||||
Toast.success('Added to Library');
|
||||
}
|
||||
break;
|
||||
case 'watched': {
|
||||
setIsWatched(prevWatched => {
|
||||
const newWatched = !prevWatched;
|
||||
AsyncStorage.setItem(`watched:${item.type}:${item.id}`, newWatched ? 'true' : 'false');
|
||||
toast(newWatched ? 'Marked as Watched' : 'Marked as Unwatched', { duration: 1200 });
|
||||
// Fire a custom event so other screens can update
|
||||
setTimeout(() => {
|
||||
DeviceEventEmitter.emit('watchedStatusChanged');
|
||||
}, 100);
|
||||
return newWatched;
|
||||
});
|
||||
const targetWatched = !isWatched;
|
||||
setIsWatched(targetWatched);
|
||||
try {
|
||||
await AsyncStorage.setItem(`watched:${item.type}:${item.id}`, targetWatched ? 'true' : 'false');
|
||||
} catch {}
|
||||
Toast.info(targetWatched ? 'Marked as Watched' : 'Marked as Unwatched');
|
||||
setTimeout(() => {
|
||||
DeviceEventEmitter.emit('watchedStatusChanged');
|
||||
}, 100);
|
||||
|
||||
// Best-effort sync: record local progress and push to Trakt if available
|
||||
if (targetWatched) {
|
||||
try {
|
||||
await storageService.setWatchProgress(
|
||||
item.id,
|
||||
item.type,
|
||||
{ currentTime: 1, duration: 1, lastUpdated: Date.now() },
|
||||
undefined,
|
||||
{ forceNotify: true, forceWrite: true }
|
||||
);
|
||||
} catch {}
|
||||
|
||||
if (item.type === 'movie') {
|
||||
try {
|
||||
const trakt = TraktService.getInstance();
|
||||
if (await trakt.isAuthenticated()) {
|
||||
await trakt.addToWatchedMovies(item.id);
|
||||
try {
|
||||
await storageService.updateTraktSyncStatus(item.id, item.type, true, 100);
|
||||
} catch {}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
setMenuVisible(false);
|
||||
break;
|
||||
}
|
||||
|
|
@ -153,7 +179,7 @@ const ContentItem = ({ item, onPress, shouldLoadImage: shouldLoadImageProp, defe
|
|||
break;
|
||||
}
|
||||
}
|
||||
}, [item, inLibrary]);
|
||||
}, [item, inLibrary, isWatched]);
|
||||
|
||||
const handleMenuClose = useCallback(() => {
|
||||
setMenuVisible(false);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { Platform } from 'react-native';
|
||||
import { toast, ToastPosition } from '@backpackapp-io/react-native-toast';
|
||||
import { Toast } from 'toastify-react-native';
|
||||
import UpdateService, { UpdateInfo } from '../services/updateService';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
|
||||
|
|
@ -78,19 +78,13 @@ export const useUpdatePopup = (): UseUpdatePopupReturn => {
|
|||
// The app will automatically reload with the new version
|
||||
console.log('Update installed successfully');
|
||||
} else {
|
||||
toast('Unable to install the update. Please try again later or check your internet connection.', {
|
||||
duration: 3000,
|
||||
position: ToastPosition.TOP,
|
||||
});
|
||||
Toast.error('Unable to install the update. Please try again later or check your internet connection.');
|
||||
// Show popup again after failed installation
|
||||
setShowUpdatePopup(true);
|
||||
}
|
||||
} catch (error) {
|
||||
if (__DEV__) console.error('Error installing update:', error);
|
||||
toast('An error occurred while installing the update. Please try again later.', {
|
||||
duration: 3000,
|
||||
position: ToastPosition.TOP,
|
||||
});
|
||||
Toast.error('An error occurred while installing the update. Please try again later.');
|
||||
// Show popup again after error
|
||||
setShowUpdatePopup(true);
|
||||
} finally {
|
||||
|
|
@ -141,12 +135,7 @@ export const useUpdatePopup = (): UseUpdatePopupReturn => {
|
|||
(async () => {
|
||||
try { await AsyncStorage.setItem(UPDATE_BADGE_KEY, 'true'); } catch {}
|
||||
})();
|
||||
try {
|
||||
toast('Update available — go to Settings → App Updates', {
|
||||
duration: 3000,
|
||||
position: ToastPosition.TOP,
|
||||
});
|
||||
} catch {}
|
||||
try { Toast.info('Update available — go to Settings → App Updates'); } catch {}
|
||||
setShowUpdatePopup(false);
|
||||
} else {
|
||||
setShowUpdatePopup(true);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import { HeaderVisibility } from '../contexts/HeaderVisibility';
|
|||
import { Stream } from '../types/streams';
|
||||
import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { useTheme } from '../contexts/ThemeContext';
|
||||
import { Toasts } from '@backpackapp-io/react-native-toast';
|
||||
import ToastManager from 'toastify-react-native';
|
||||
import { PostHogProvider } from 'posthog-react-native';
|
||||
|
||||
// Import screens with their proper types
|
||||
|
|
@ -889,6 +889,7 @@ const customFadeInterpolator = ({ current, layouts }: any) => {
|
|||
const InnerNavigator = ({ initialRouteName }: { initialRouteName?: keyof RootStackParamList }) => {
|
||||
const { currentTheme } = useTheme();
|
||||
const { user, loading } = useAccount();
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
// Handle Android-specific optimizations
|
||||
useEffect(() => {
|
||||
|
|
@ -1344,7 +1345,85 @@ const InnerNavigator = ({ initialRouteName }: { initialRouteName?: keyof RootSta
|
|||
</Stack.Navigator>
|
||||
</View>
|
||||
</PaperProvider>
|
||||
<Toasts />
|
||||
{/* Global toast customization using ThemeContext */}
|
||||
<ToastManager
|
||||
position="top"
|
||||
useModal={false}
|
||||
theme={'dark'}
|
||||
// Dimensions
|
||||
width={'90%'}
|
||||
minHeight={61}
|
||||
// Icon defaults
|
||||
iconFamily="MaterialIcons"
|
||||
iconSize={22}
|
||||
icons={{
|
||||
success: 'check-circle',
|
||||
error: 'error',
|
||||
info: 'info',
|
||||
warn: 'warning',
|
||||
default: 'notifications',
|
||||
}}
|
||||
// Close icon defaults
|
||||
showCloseIcon={true}
|
||||
closeIcon={'close'}
|
||||
closeIconFamily={'MaterialIcons'}
|
||||
closeIconSize={18}
|
||||
// Spacing (ensure below safe area)
|
||||
topOffset={Math.max(8, insets.top + 8)}
|
||||
bottomOffset={40}
|
||||
// Styles bound to ThemeContext
|
||||
style={{
|
||||
backgroundColor: currentTheme.colors.darkBackground,
|
||||
borderRadius: 12,
|
||||
paddingVertical: 12,
|
||||
paddingHorizontal: 14,
|
||||
}}
|
||||
textStyle={{
|
||||
color: currentTheme.colors.highEmphasis,
|
||||
fontWeight: '600',
|
||||
}}
|
||||
config={{
|
||||
default: (props: any) => (
|
||||
<View style={{
|
||||
backgroundColor: currentTheme.colors.elevation2,
|
||||
borderRadius: 12,
|
||||
padding: 12,
|
||||
width: '100%'
|
||||
}}>
|
||||
<Text style={{ color: currentTheme.colors.highEmphasis, fontWeight: '700' }}>{props.text1}</Text>
|
||||
{props.text2 ? (
|
||||
<Text style={{ color: currentTheme.colors.mediumEmphasis, marginTop: 4 }}>{props.text2}</Text>
|
||||
) : null}
|
||||
</View>
|
||||
),
|
||||
success: (props: any) => (
|
||||
<View style={{
|
||||
backgroundColor: currentTheme.colors.elevation2,
|
||||
borderRadius: 12,
|
||||
padding: 12,
|
||||
width: '100%'
|
||||
}}>
|
||||
<Text style={{ color: currentTheme.colors.success || '#4CAF50', fontWeight: '800' }}>{props.text1}</Text>
|
||||
{props.text2 ? (
|
||||
<Text style={{ color: currentTheme.colors.mediumEmphasis, marginTop: 4 }}>{props.text2}</Text>
|
||||
) : null}
|
||||
</View>
|
||||
),
|
||||
error: (props: any) => (
|
||||
<View style={{
|
||||
backgroundColor: currentTheme.colors.elevation2,
|
||||
borderRadius: 12,
|
||||
padding: 12,
|
||||
width: '100%'
|
||||
}}>
|
||||
<Text style={{ color: currentTheme.colors.error || '#ff4444', fontWeight: '800' }}>{props.text1}</Text>
|
||||
{props.text2 ? (
|
||||
<Text style={{ color: currentTheme.colors.mediumEmphasis, marginTop: 4 }}>{props.text2}</Text>
|
||||
) : null}
|
||||
</View>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</SafeAreaProvider>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { useTheme } from '../contexts/ThemeContext';
|
|||
import { useAccount } from '../contexts/AccountContext';
|
||||
import { useNavigation, useRoute } from '@react-navigation/native';
|
||||
import * as Haptics from 'expo-haptics';
|
||||
import { toast } from '@backpackapp-io/react-native-toast';
|
||||
import ToastManager, { Toast } from 'toastify-react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
||||
const { width, height } = Dimensions.get('window');
|
||||
|
|
@ -144,21 +144,21 @@ const AuthScreen: React.FC = () => {
|
|||
if (!isEmailValid) {
|
||||
const msg = 'Enter a valid email address';
|
||||
setError(msg);
|
||||
toast.error(msg);
|
||||
Toast.error(msg);
|
||||
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error).catch(() => {});
|
||||
return;
|
||||
}
|
||||
if (!isPasswordValid) {
|
||||
const msg = 'Password must be at least 6 characters';
|
||||
setError(msg);
|
||||
toast.error(msg);
|
||||
Toast.error(msg);
|
||||
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error).catch(() => {});
|
||||
return;
|
||||
}
|
||||
if (mode === 'signup' && !passwordsMatch) {
|
||||
const msg = 'Passwords do not match';
|
||||
setError(msg);
|
||||
toast.error(msg);
|
||||
Toast.error(msg);
|
||||
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error).catch(() => {});
|
||||
return;
|
||||
}
|
||||
|
|
@ -167,11 +167,11 @@ const AuthScreen: React.FC = () => {
|
|||
const err = mode === 'signin' ? await signIn(email.trim(), password) : await signUp(email.trim(), password);
|
||||
if (err) {
|
||||
setError(err);
|
||||
toast.error(err);
|
||||
Toast.error(err);
|
||||
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error).catch(() => {});
|
||||
} else {
|
||||
const msg = mode === 'signin' ? 'Logged in successfully' : 'Sign up successful';
|
||||
toast.success(msg);
|
||||
Toast.success(msg);
|
||||
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success).catch(() => {});
|
||||
|
||||
// Navigate to main tabs after successful authentication
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ import { useLoading } from '../contexts/LoadingContext';
|
|||
import * as ScreenOrientation from 'expo-screen-orientation';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { toast, ToastPosition } from '@backpackapp-io/react-native-toast';
|
||||
import { Toast } from 'toastify-react-native';
|
||||
import FirstTimeWelcome from '../components/FirstTimeWelcome';
|
||||
import { imageCacheService } from '../services/imageCacheService';
|
||||
import { HeaderVisibility } from '../contexts/HeaderVisibility';
|
||||
|
|
@ -341,12 +341,7 @@ const HomeScreen = () => {
|
|||
await AsyncStorage.removeItem('showLoginHintToastOnce');
|
||||
hideTimer = setTimeout(() => setHintVisible(false), 2000);
|
||||
// Also show a global toast for consistency across screens
|
||||
try {
|
||||
toast('You can sign in anytime from Settings → Account', {
|
||||
duration: 1600,
|
||||
position: ToastPosition.BOTTOM,
|
||||
});
|
||||
} catch {}
|
||||
try { Toast.info('You can sign in anytime from Settings → Account', 'bottom'); } catch {}
|
||||
}
|
||||
} catch {}
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React, { useState, useEffect, useMemo, useCallback } from 'react';
|
|||
import { DeviceEventEmitter } from 'react-native';
|
||||
import { Share } from 'react-native';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { toast } from '@backpackapp-io/react-native-toast';
|
||||
import { Toast } from 'toastify-react-native';
|
||||
import DropUpMenu from '../components/home/DropUpMenu';
|
||||
import {
|
||||
View,
|
||||
|
|
@ -1002,11 +1002,11 @@ const LibraryScreen = () => {
|
|||
case 'library': {
|
||||
try {
|
||||
await catalogService.removeFromLibrary(selectedItem.type, selectedItem.id);
|
||||
toast('Removed from Library', { duration: 1200 });
|
||||
Toast.info('Removed from Library');
|
||||
setLibraryItems(prev => prev.filter(item => !(item.id === selectedItem.id && item.type === selectedItem.type)));
|
||||
setMenuVisible(false);
|
||||
} catch (error) {
|
||||
toast('Failed to update Library', { duration: 1200 });
|
||||
Toast.error('Failed to update Library');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -1016,7 +1016,7 @@ const LibraryScreen = () => {
|
|||
const key = `watched:${selectedItem.type}:${selectedItem.id}`;
|
||||
const newWatched = !selectedItem.watched;
|
||||
await AsyncStorage.setItem(key, newWatched ? 'true' : 'false');
|
||||
toast(newWatched ? 'Marked as Watched' : 'Marked as Unwatched', { duration: 1200 });
|
||||
Toast.info(newWatched ? 'Marked as Watched' : 'Marked as Unwatched');
|
||||
// Instantly update local state
|
||||
setLibraryItems(prev => prev.map(item =>
|
||||
item.id === selectedItem.id && item.type === selectedItem.type
|
||||
|
|
|
|||
|
|
@ -531,12 +531,6 @@ const SearchScreen = () => {
|
|||
<MaterialIcons name="check-circle" size={20} color={currentTheme.colors.success || '#4CAF50'} />
|
||||
</View>
|
||||
)}
|
||||
{/* 'series'/'movie' text in original place */}
|
||||
<View style={styles.itemTypeContainer}>
|
||||
<Text style={[styles.itemTypeText, { color: currentTheme.colors.white }]}>
|
||||
{item.type === 'movie' ? 'MOVIE' : 'SERIES'}
|
||||
</Text>
|
||||
</View>
|
||||
{item.imdbRating && (
|
||||
<View style={styles.ratingContainer}>
|
||||
<MaterialIcons name="star" size={12} color="#FFC107" />
|
||||
|
|
@ -1038,19 +1032,6 @@ const styles = StyleSheet.create({
|
|||
marginBottom: 16,
|
||||
borderRadius: 4,
|
||||
},
|
||||
itemTypeContainer: {
|
||||
position: 'absolute',
|
||||
top: 8,
|
||||
left: 8,
|
||||
backgroundColor: 'rgba(0,0,0,0.7)',
|
||||
paddingHorizontal: 6,
|
||||
paddingVertical: 2,
|
||||
borderRadius: 4,
|
||||
},
|
||||
itemTypeText: {
|
||||
fontSize: isTablet ? 7 : 8,
|
||||
fontWeight: '700',
|
||||
},
|
||||
ratingContainer: {
|
||||
position: 'absolute',
|
||||
bottom: 8,
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ import QualityBadge from '../components/metadata/QualityBadge';
|
|||
import { logger } from '../utils/logger';
|
||||
import { isMkvStream } from '../utils/mkvDetection';
|
||||
import CustomAlert from '../components/CustomAlert';
|
||||
import { toast, ToastPosition } from '@backpackapp-io/react-native-toast';
|
||||
import { Toast } from 'toastify-react-native';
|
||||
import { useDownloads } from '../contexts/DownloadsContext';
|
||||
|
||||
const TMDB_LOGO = 'https://upload.wikimedia.org/wikipedia/commons/thumb/8/89/Tmdb.new.logo.svg/512px-Tmdb.new.logo.svg.png?20200406190906';
|
||||
|
|
@ -233,10 +233,7 @@ const StreamCard = memo(({ stream, onPress, index, isLoading, statusMessage, the
|
|||
|
||||
// Use toast for Android, custom alert for iOS
|
||||
if (Platform.OS === 'android') {
|
||||
toast('Stream URL copied to clipboard!', {
|
||||
duration: 2000,
|
||||
position: ToastPosition.BOTTOM,
|
||||
});
|
||||
Toast.success('Stream URL copied to clipboard!', 'bottom');
|
||||
} else {
|
||||
// iOS uses custom alert
|
||||
setTimeout(() => {
|
||||
|
|
@ -246,10 +243,7 @@ const StreamCard = memo(({ stream, onPress, index, isLoading, statusMessage, the
|
|||
} catch (error) {
|
||||
// Fallback: show URL in alert if clipboard fails
|
||||
if (Platform.OS === 'android') {
|
||||
toast(`Stream URL: ${stream.url}`, {
|
||||
duration: 3000,
|
||||
position: ToastPosition.BOTTOM,
|
||||
});
|
||||
Toast.info(`Stream URL: ${stream.url}`, 'bottom');
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
showAlert('Stream URL', stream.url);
|
||||
|
|
@ -322,7 +316,7 @@ const StreamCard = memo(({ stream, onPress, index, isLoading, statusMessage, the
|
|||
url,
|
||||
headers: (stream.headers as any) || undefined,
|
||||
});
|
||||
toast('Download started', { duration: 1500, position: ToastPosition.BOTTOM });
|
||||
Toast.success('Download started', 'bottom');
|
||||
} catch {}
|
||||
}, [startDownload, stream.url, stream.headers, streamInfo.quality, showAlert, stream.name, stream.title]);
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
Dimensions,
|
||||
Linking
|
||||
} from 'react-native';
|
||||
import { toast, ToastPosition } from '@backpackapp-io/react-native-toast';
|
||||
import { Toast } from 'toastify-react-native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { NavigationProp } from '@react-navigation/native';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
|
|
@ -152,9 +152,7 @@ const UpdateScreen: React.FC = () => {
|
|||
// Also refresh GitHub section on mount (works in dev and prod)
|
||||
try { github.refresh(); } catch {}
|
||||
if (Platform.OS === 'android') {
|
||||
try {
|
||||
toast('Checking for updates…', { duration: 1200, position: ToastPosition.TOP });
|
||||
} catch {}
|
||||
try { Toast.info('Checking for updates…'); } catch {}
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue