mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-21 00:32:04 +00:00
Pluginscreen UI changes
This commit is contained in:
parent
9ae0a7010c
commit
bbbc22f30f
5 changed files with 87 additions and 35 deletions
|
|
@ -57,6 +57,8 @@ export interface AppSettings {
|
||||||
excludedQualities: string[]; // Array of quality strings to exclude (e.g., ['2160p', '4K', '1080p', '720p'])
|
excludedQualities: string[]; // Array of quality strings to exclude (e.g., ['2160p', '4K', '1080p', '720p'])
|
||||||
// Playback behavior
|
// Playback behavior
|
||||||
alwaysResume: boolean; // If true, resume automatically without prompt when progress < 85%
|
alwaysResume: boolean; // If true, resume automatically without prompt when progress < 85%
|
||||||
|
// Downloads
|
||||||
|
enableDownloads: boolean; // Show Downloads tab and enable saving streams
|
||||||
// Theme settings
|
// Theme settings
|
||||||
themeId: string;
|
themeId: string;
|
||||||
customThemes: CustomThemeDef[];
|
customThemes: CustomThemeDef[];
|
||||||
|
|
@ -105,6 +107,8 @@ export const DEFAULT_SETTINGS: AppSettings = {
|
||||||
excludedQualities: [], // No qualities excluded by default
|
excludedQualities: [], // No qualities excluded by default
|
||||||
// Playback behavior defaults
|
// Playback behavior defaults
|
||||||
alwaysResume: true,
|
alwaysResume: true,
|
||||||
|
// Downloads
|
||||||
|
enableDownloads: false,
|
||||||
// Theme defaults
|
// Theme defaults
|
||||||
themeId: 'default',
|
themeId: 'default',
|
||||||
customThemes: [],
|
customThemes: [],
|
||||||
|
|
|
||||||
|
|
@ -458,6 +458,9 @@ const WrappedScreen: React.FC<{Screen: React.ComponentType<any>}> = ({ Screen })
|
||||||
// Tab Navigator
|
// Tab Navigator
|
||||||
const MainTabs = () => {
|
const MainTabs = () => {
|
||||||
const { currentTheme } = useTheme();
|
const { currentTheme } = useTheme();
|
||||||
|
const { settings } = require('../hooks/useSettings');
|
||||||
|
const { useSettings: useSettingsHook } = require('../hooks/useSettings');
|
||||||
|
const { settings: appSettings } = useSettingsHook();
|
||||||
const [hasUpdateBadge, setHasUpdateBadge] = React.useState(false);
|
const [hasUpdateBadge, setHasUpdateBadge] = React.useState(false);
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (Platform.OS !== 'android') return;
|
if (Platform.OS !== 'android') return;
|
||||||
|
|
@ -829,16 +832,18 @@ const MainTabs = () => {
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tab.Screen
|
{appSettings?.enableDownloads !== false && (
|
||||||
name="Downloads"
|
<Tab.Screen
|
||||||
component={DownloadsScreen}
|
name="Downloads"
|
||||||
options={{
|
component={DownloadsScreen}
|
||||||
tabBarLabel: 'Downloads',
|
options={{
|
||||||
tabBarIcon: ({ color, size, focused }) => (
|
tabBarLabel: 'Downloads',
|
||||||
<MaterialCommunityIcons name={focused ? 'download' : 'download-outline'} size={size} color={color} />
|
tabBarIcon: ({ color, size, focused }) => (
|
||||||
),
|
<MaterialCommunityIcons name={focused ? 'download' : 'download-outline'} size={size} color={color} />
|
||||||
}}
|
),
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Tab.Screen
|
<Tab.Screen
|
||||||
name="Settings"
|
name="Settings"
|
||||||
component={SettingsScreen}
|
component={SettingsScreen}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ const ANDROID_STATUSBAR_HEIGHT = StatusBar.currentHeight || 0;
|
||||||
const createStyles = (colors: any) => StyleSheet.create({
|
const createStyles = (colors: any) => StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
backgroundColor: colors.background,
|
backgroundColor: colors.darkBackground,
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
|
@ -63,7 +63,7 @@ const createStyles = (colors: any) => StyleSheet.create({
|
||||||
headerTitle: {
|
headerTitle: {
|
||||||
fontSize: 34,
|
fontSize: 34,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
color: colors.white,
|
color: colors.text,
|
||||||
paddingHorizontal: 16,
|
paddingHorizontal: 16,
|
||||||
marginBottom: 24,
|
marginBottom: 24,
|
||||||
},
|
},
|
||||||
|
|
@ -71,7 +71,7 @@ const createStyles = (colors: any) => StyleSheet.create({
|
||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
section: {
|
section: {
|
||||||
backgroundColor: colors.darkBackground,
|
backgroundColor: colors.elevation1,
|
||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
padding: 16,
|
padding: 16,
|
||||||
|
|
@ -852,6 +852,7 @@ const PluginsScreen: React.FC = () => {
|
||||||
const [showboxUiToken, setShowboxUiToken] = useState<string>('');
|
const [showboxUiToken, setShowboxUiToken] = useState<string>('');
|
||||||
const [showboxSavedToken, setShowboxSavedToken] = useState<string>('');
|
const [showboxSavedToken, setShowboxSavedToken] = useState<string>('');
|
||||||
const [showboxScraperId, setShowboxScraperId] = useState<string | null>(null);
|
const [showboxScraperId, setShowboxScraperId] = useState<string | null>(null);
|
||||||
|
const [showboxTokenVisible, setShowboxTokenVisible] = useState<boolean>(false);
|
||||||
|
|
||||||
// Multiple repositories state
|
// Multiple repositories state
|
||||||
const [repositories, setRepositories] = useState<RepositoryInfo[]>([]);
|
const [repositories, setRepositories] = useState<RepositoryInfo[]>([]);
|
||||||
|
|
@ -1084,10 +1085,12 @@ const PluginsScreen: React.FC = () => {
|
||||||
const s = await localScraperService.getScraperSettings(sb.id);
|
const s = await localScraperService.getScraperSettings(sb.id);
|
||||||
setShowboxUiToken(s.uiToken || '');
|
setShowboxUiToken(s.uiToken || '');
|
||||||
setShowboxSavedToken(s.uiToken || '');
|
setShowboxSavedToken(s.uiToken || '');
|
||||||
|
setShowboxTokenVisible(false);
|
||||||
} else {
|
} else {
|
||||||
setShowboxScraperId(null);
|
setShowboxScraperId(null);
|
||||||
setShowboxUiToken('');
|
setShowboxUiToken('');
|
||||||
setShowboxSavedToken('');
|
setShowboxSavedToken('');
|
||||||
|
setShowboxTokenVisible(false);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('[ScraperSettings] Failed to load scrapers:', error);
|
logger.error('[ScraperSettings] Failed to load scrapers:', error);
|
||||||
|
|
@ -1630,17 +1633,25 @@ const PluginsScreen: React.FC = () => {
|
||||||
{showboxScraperId && scraper.id === showboxScraperId && settings.enableLocalScrapers && (
|
{showboxScraperId && scraper.id === showboxScraperId && settings.enableLocalScrapers && (
|
||||||
<View style={{ marginTop: 16, paddingTop: 16, borderTopWidth: 1, borderTopColor: colors.elevation3 }}>
|
<View style={{ marginTop: 16, paddingTop: 16, borderTopWidth: 1, borderTopColor: colors.elevation3 }}>
|
||||||
<Text style={[styles.settingTitle, { marginBottom: 8 }]}>ShowBox UI Token</Text>
|
<Text style={[styles.settingTitle, { marginBottom: 8 }]}>ShowBox UI Token</Text>
|
||||||
<TextInput
|
<View style={{ flexDirection: 'row', alignItems: 'center', marginBottom: 12 }}>
|
||||||
style={[styles.textInput, { marginBottom: 12 }]}
|
<TextInput
|
||||||
value={showboxUiToken}
|
style={[styles.textInput, { flex: 1, marginBottom: 0 }]}
|
||||||
onChangeText={setShowboxUiToken}
|
value={showboxUiToken}
|
||||||
placeholder="Paste your ShowBox UI token"
|
onChangeText={setShowboxUiToken}
|
||||||
placeholderTextColor={colors.mediumGray}
|
placeholder="Paste your ShowBox UI token"
|
||||||
autoCapitalize="none"
|
placeholderTextColor={colors.mediumGray}
|
||||||
autoCorrect={false}
|
autoCapitalize="none"
|
||||||
multiline={true}
|
autoCorrect={false}
|
||||||
numberOfLines={3}
|
secureTextEntry={showboxSavedToken.length > 0 && !showboxTokenVisible}
|
||||||
/>
|
multiline={false}
|
||||||
|
numberOfLines={1}
|
||||||
|
/>
|
||||||
|
{showboxSavedToken.length > 0 && (
|
||||||
|
<TouchableOpacity onPress={() => setShowboxTokenVisible(v => !v)} accessibilityRole="button" accessibilityLabel={showboxTokenVisible ? 'Hide token' : 'Show token'} style={{ marginLeft: 10 }}>
|
||||||
|
<Ionicons name={showboxTokenVisible ? 'eye-off' : 'eye'} size={18} color={colors.primary} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
<View style={styles.buttonRow}>
|
<View style={styles.buttonRow}>
|
||||||
{showboxUiToken !== showboxSavedToken && (
|
{showboxUiToken !== showboxSavedToken && (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
|
|
|
||||||
|
|
@ -278,6 +278,8 @@ const SettingsScreen: React.FC = () => {
|
||||||
|
|
||||||
// Tablet-specific state
|
// Tablet-specific state
|
||||||
const [selectedCategory, setSelectedCategory] = useState('account');
|
const [selectedCategory, setSelectedCategory] = useState('account');
|
||||||
|
const [downloadsDevUnlocked, setDownloadsDevUnlocked] = useState(false);
|
||||||
|
const [versionTapCount, setVersionTapCount] = useState(0);
|
||||||
|
|
||||||
// States for dynamic content
|
// States for dynamic content
|
||||||
const [addonCount, setAddonCount] = useState<number>(0);
|
const [addonCount, setAddonCount] = useState<number>(0);
|
||||||
|
|
@ -613,6 +615,22 @@ const SettingsScreen: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
isTablet={isTablet}
|
isTablet={isTablet}
|
||||||
/>
|
/>
|
||||||
|
{downloadsDevUnlocked && (
|
||||||
|
<SettingItem
|
||||||
|
title="Enable Downloads"
|
||||||
|
description="Show Downloads tab and enable saving streams"
|
||||||
|
icon="download"
|
||||||
|
renderControl={() => (
|
||||||
|
<Switch
|
||||||
|
value={settings?.enableDownloads ?? true}
|
||||||
|
onValueChange={(value) => updateSetting('enableDownloads', value)}
|
||||||
|
trackColor={{ false: 'rgba(255,255,255,0.2)', true: currentTheme.colors.primary }}
|
||||||
|
thumbColor={settings?.enableDownloads ? '#fff' : '#f4f3f4'}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
isTablet={isTablet}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<SettingItem
|
<SettingItem
|
||||||
title="Notifications"
|
title="Notifications"
|
||||||
description="Episode reminders"
|
description="Episode reminders"
|
||||||
|
|
@ -646,6 +664,16 @@ const SettingsScreen: React.FC = () => {
|
||||||
title="Version"
|
title="Version"
|
||||||
description={getDisplayedAppVersion()}
|
description={getDisplayedAppVersion()}
|
||||||
icon="info-outline"
|
icon="info-outline"
|
||||||
|
onPress={() => {
|
||||||
|
if (downloadsDevUnlocked) return;
|
||||||
|
const next = versionTapCount + 1;
|
||||||
|
setVersionTapCount(next);
|
||||||
|
if (next >= 5) {
|
||||||
|
setDownloadsDevUnlocked(true);
|
||||||
|
setVersionTapCount(0);
|
||||||
|
openAlert('Developer option unlocked', 'Downloads toggle is now visible in Playback settings.');
|
||||||
|
}
|
||||||
|
}}
|
||||||
isLast={true}
|
isLast={true}
|
||||||
isTablet={isTablet}
|
isTablet={isTablet}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -221,6 +221,8 @@ const StreamCard = memo(({ stream, onPress, index, isLoading, statusMessage, the
|
||||||
parentPosterUrl?: string | null;
|
parentPosterUrl?: string | null;
|
||||||
providerName?: string;
|
providerName?: string;
|
||||||
}) => {
|
}) => {
|
||||||
|
const { useSettings } = require('../hooks/useSettings');
|
||||||
|
const { settings } = useSettings();
|
||||||
const { startDownload } = useDownloads();
|
const { startDownload } = useDownloads();
|
||||||
|
|
||||||
// Handle long press to copy stream URL to clipboard
|
// Handle long press to copy stream URL to clipboard
|
||||||
|
|
@ -398,17 +400,19 @@ const StreamCard = memo(({ stream, onPress, index, isLoading, statusMessage, the
|
||||||
color={theme.colors.white}
|
color={theme.colors.white}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<TouchableOpacity
|
{settings?.enableDownloads !== false && (
|
||||||
style={[styles.streamAction, { marginLeft: 8, backgroundColor: theme.colors.elevation2 }]}
|
<TouchableOpacity
|
||||||
onPress={handleDownload}
|
style={[styles.streamAction, { marginLeft: 8, backgroundColor: theme.colors.elevation2 }]}
|
||||||
activeOpacity={0.7}
|
onPress={handleDownload}
|
||||||
>
|
activeOpacity={0.7}
|
||||||
<MaterialIcons
|
>
|
||||||
name="download"
|
<MaterialIcons
|
||||||
size={20}
|
name="download"
|
||||||
color={theme.colors.highEmphasis}
|
size={20}
|
||||||
/>
|
color={theme.colors.highEmphasis}
|
||||||
</TouchableOpacity>
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue