add forgot password link to AuthScreen and enhance URL normalization in SupabaseSyncService

This commit is contained in:
tapframe 2026-02-19 06:23:08 +05:30
parent 9cab1f4f1e
commit 9b330b8226
3 changed files with 48 additions and 2 deletions

View file

@ -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;

View file

@ -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);
} }