mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-03-11 17:45:38 +00:00
add forgot password link to AuthScreen and enhance URL normalization in SupabaseSyncService
This commit is contained in:
parent
9cab1f4f1e
commit
9b330b8226
3 changed files with 48 additions and 2 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { View, TextInput, Text, TouchableOpacity, StyleSheet, ActivityIndicator, SafeAreaView, KeyboardAvoidingView, Platform, Animated, Easing, Keyboard, StatusBar, useWindowDimensions } from 'react-native';
|
import { View, TextInput, Text, TouchableOpacity, StyleSheet, ActivityIndicator, SafeAreaView, KeyboardAvoidingView, Platform, Animated, Easing, Keyboard, StatusBar, useWindowDimensions, Linking } from 'react-native';
|
||||||
import { mmkvStorage } from '../services/mmkvStorage';
|
import { mmkvStorage } from '../services/mmkvStorage';
|
||||||
import { LinearGradient } from 'expo-linear-gradient';
|
import { LinearGradient } from 'expo-linear-gradient';
|
||||||
import { MaterialIcons } from '@expo/vector-icons';
|
import { MaterialIcons } from '@expo/vector-icons';
|
||||||
|
|
@ -425,6 +425,16 @@ const AuthScreen: React.FC = () => {
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
{mode === 'signin' && (
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => Linking.openURL('https://nuvioapp.space/account/reset-password')}
|
||||||
|
activeOpacity={0.75}
|
||||||
|
style={styles.forgotPasswordButton}
|
||||||
|
>
|
||||||
|
<Text style={[styles.forgotPasswordText, { color: currentTheme.colors.textMuted }]}>Forgot password?</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Confirm Password (signup only) */}
|
{/* Confirm Password (signup only) */}
|
||||||
{mode === 'signup' && (
|
{mode === 'signup' && (
|
||||||
<View style={styles.inputContainer}>
|
<View style={styles.inputContainer}>
|
||||||
|
|
@ -744,6 +754,15 @@ const styles = StyleSheet.create({
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
},
|
},
|
||||||
|
forgotPasswordButton: {
|
||||||
|
alignSelf: 'flex-end',
|
||||||
|
marginTop: -6,
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
|
forgotPasswordText: {
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: '600',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default AuthScreen;
|
export default AuthScreen;
|
||||||
|
|
|
||||||
|
|
@ -825,7 +825,11 @@ class SupabaseSyncService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private normalizeUrl(url: string): string {
|
private normalizeUrl(url: string): string {
|
||||||
return url.trim().toLowerCase();
|
let u = url.trim().toLowerCase();
|
||||||
|
|
||||||
|
u = u.replace(/\/manifest\.json\/?$/i, '');
|
||||||
|
u = u.replace(/\/+$/, '');
|
||||||
|
return u;
|
||||||
}
|
}
|
||||||
|
|
||||||
private toBigIntNumber(value: unknown): number {
|
private toBigIntNumber(value: unknown): number {
|
||||||
|
|
@ -1063,14 +1067,37 @@ class SupabaseSyncService {
|
||||||
.map((url) => this.normalizeUrl(url))
|
.map((url) => this.normalizeUrl(url))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Build a set of currently-installed addon manifest IDs so we can also
|
||||||
|
// skip by ID (prevents duplicate installations of stream-providing addons
|
||||||
|
// that the URL check alone might miss due to URL format differences).
|
||||||
|
const installedAddonIds = new Set(
|
||||||
|
installed.map((addon) => addon.id).filter(Boolean)
|
||||||
|
);
|
||||||
|
|
||||||
for (const row of rows || []) {
|
for (const row of rows || []) {
|
||||||
if (!row.url) continue;
|
if (!row.url) continue;
|
||||||
const normalized = this.normalizeUrl(row.url);
|
const normalized = this.normalizeUrl(row.url);
|
||||||
if (installedUrls.has(normalized)) continue;
|
if (installedUrls.has(normalized)) continue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Pre-check: fetch manifest to see if this addon ID is already installed.
|
||||||
|
// This prevents creating duplicate installations for stream-providing
|
||||||
|
// addons whose URLs differ only by format (e.g. with/without manifest.json).
|
||||||
|
let manifest: Manifest | null = null;
|
||||||
|
try {
|
||||||
|
manifest = await stremioService.getManifest(row.url);
|
||||||
|
} catch {
|
||||||
|
// If manifest fetch fails, fall through to installAddon which will also fail and be caught below.
|
||||||
|
}
|
||||||
|
if (manifest?.id && installedAddonIds.has(manifest.id)) {
|
||||||
|
// Addon already installed under a different URL variant — skip.
|
||||||
|
logger.log(`[SupabaseSyncService] pullAddonsToLocal: skipping duplicate addon id=${manifest.id} url=${row.url}`);
|
||||||
|
installedUrls.add(normalized);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
await stremioService.installAddon(row.url);
|
await stremioService.installAddon(row.url);
|
||||||
installedUrls.add(normalized);
|
installedUrls.add(normalized);
|
||||||
|
if (manifest?.id) installedAddonIds.add(manifest.id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.warn('[SupabaseSyncService] Failed to install synced addon:', row.url, error);
|
logger.warn('[SupabaseSyncService] Failed to install synced addon:', row.url, error);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue