diff --git a/app/index.tsx b/app/index.tsx index 4f12446..058d1f4 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -1,145 +1,36 @@ - - -// app/index.tsx - TELA DE LOGIN - - -import { Link, useRouter } from 'expo-router'; -import { useState } from 'react'; -import { - ActivityIndicator, - Alert, - KeyboardAvoidingView, - Platform, - StyleSheet, - Text, - TextInput, - TouchableOpacity, - View -} from 'react-native'; +// app/index.tsx +import { useRouter } from 'expo-router'; +import { KeyboardAvoidingView, Platform, ScrollView, StyleSheet, Text, View } from 'react-native'; +import Auth from '../components/Auth'; export default function LoginScreen() { - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [loading, setLoading] = useState(false); + const router = useRouter(); - const router = useRouter(); // Inicializa o router - - const handleLogin = () => { - if (!email || !password) { - Alert.alert('Atenção', 'Por favor, preencha todos os campos'); - return; - } - - if (!email.includes('@')) { - Alert.alert('Email inválido', 'Por favor, insira um email válido'); - return; - } - - setLoading(true); - - // SIMULAÇÃO DE LOGIN - setTimeout(() => { - setLoading(false); - - // Primeiro navega para a dashboard - router.replace('/AlunoHome'); // ⬅️ Certifica-te que o ficheiro é app/dashboard.tsx - - // Depois mostra alert de boas-vindas (opcional) - setTimeout(() => { - Alert.alert('Login realizado!', `Bem-vindo(a), ${email.split('@')[0]}!`); - }, 300); // delay pequeno para garantir que a navegação ocorreu - - }, 1500); + const handleLoginSuccess = () => { + router.replace('/AlunoHome'); }; return ( - - - - {/* LOGO/TÍTULO */} - - 📱 Estágios+ - Escola Profissional de Vila do Conde + + + + + 📱 Estágios+ + Escola Profissional de Vila do Conde + + + {/* Componente Auth */} + - - {/* FORMULÁRIO */} - - Email - - - Palavra-passe - - - {/* BOTÃO ENTRAR */} - - {loading ? ( - - ) : ( - ENTRAR - )} - - - {/* LINK ESQUECI SENHA */} - - Esqueceu-se da palavra-passe? - - - - {/* CADASTRO */} - - Não tem uma conta? - - - Crie uma conta agora - - - - - + ); } -// ESTILOS const styles = StyleSheet.create({ - container: { flex: 1, backgroundColor: '#f8f9fa' }, - content: { flex: 1, justifyContent: 'center', paddingHorizontal: 24 }, + scrollContainer: { flexGrow: 1, justifyContent: 'center', paddingHorizontal: 24, paddingVertical: 40 }, + content: { flex: 1, justifyContent: 'center' }, header: { alignItems: 'center', marginBottom: 48 }, title: { fontSize: 32, fontWeight: '800', color: '#2d3436', marginBottom: 8 }, subtitle: { fontSize: 16, color: '#636e72', textAlign: 'center' }, - form: { backgroundColor: '#fff', borderRadius: 20, padding: 24, shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.1, shadowRadius: 12, elevation: 5 }, - label: { fontSize: 14, fontWeight: '600', color: '#2d3436', marginBottom: 8, marginLeft: 4 }, - input: { backgroundColor: '#f8f9fa', borderRadius: 12, paddingHorizontal: 16, paddingVertical: 14, fontSize: 16, marginBottom: 20, borderWidth: 1, borderColor: '#dfe6e9', color: '#2d3436' }, - button: { backgroundColor: '#0984e3', borderRadius: 12, paddingVertical: 16, alignItems: 'center', marginTop: 8, marginBottom: 24 }, - buttonDisabled: { backgroundColor: '#74b9ff' }, - buttonText: { color: '#fff', fontSize: 16, fontWeight: '700' }, - forgotLink: { alignItems: 'center' }, - forgotText: { color: '#0984e3', fontSize: 15, fontWeight: '500' }, - footer: { flexDirection: 'row', justifyContent: 'center', marginTop: 40, paddingTop: 24, borderTopWidth: 1, borderTopColor: '#dfe6e9' }, - footerText: { color: '#636e72', fontSize: 15 }, - registerText: { color: '#0984e3', fontSize: 15, fontWeight: '700' }, -}); \ No newline at end of file +}); diff --git a/app/lib/supabase.ts b/app/lib/supabase.ts new file mode 100644 index 0000000..b6aff2a --- /dev/null +++ b/app/lib/supabase.ts @@ -0,0 +1,32 @@ +import AsyncStorage from '@react-native-async-storage/async-storage' +import { createClient, processLock } from '@supabase/supabase-js' +import { AppState, Platform } from 'react-native' +import 'react-native-url-polyfill/auto' + +const supabaseUrl = 'https://ssorfpctjeujolmtkfib.supabase.co' +const supabaseAnonKey = 'sb_publishable_SDocGprdYkUKi04FyfVqmA_Ykirp9cK' + +export const supabase = createClient(supabaseUrl, supabaseAnonKey, { + auth: { + ...(Platform.OS !== "web" ? { storage: AsyncStorage } : {}), + autoRefreshToken: true, + persistSession: true, + detectSessionInUrl: false, + lock: processLock, + }, +}) + +// Tells Supabase Auth to continuously refresh the session automatically +// if the app is in the foreground. When this is added, you will continue +// to receive `onAuthStateChange` events with the `TOKEN_REFRESHED` or +// `SIGNED_OUT` event if the user's session is terminated. This should +// only be registered once. +if (Platform.OS !== "web") { + AppState.addEventListener('change', (state) => { + if (state === 'active') { + supabase.auth.startAutoRefresh() + } else { + supabase.auth.stopAutoRefresh() + } + }) +} \ No newline at end of file diff --git a/app/redefenirsenha.tsx b/app/redefenirsenha.tsx new file mode 100644 index 0000000..7bde524 --- /dev/null +++ b/app/redefenirsenha.tsx @@ -0,0 +1,131 @@ +// app/forgot-password.tsx +import { useRouter } from 'expo-router'; +import { useState } from 'react'; +import { + ActivityIndicator, + Alert, + KeyboardAvoidingView, + Platform, + ScrollView, + StyleSheet, + Text, + TextInput, + TouchableOpacity, + View +} from 'react-native'; +import { supabase } from '../app/lib/supabase'; + +export default function ForgotPassword() { + const [email, setEmail] = useState(''); + const [loading, setLoading] = useState(false); + const router = useRouter(); + + const handleSendResetEmail = async () => { + if (!email) { + Alert.alert('Atenção', 'Insira seu email'); + return; + } + + setLoading(true); + try { + const { error } = await supabase.auth.resetPasswordForEmail(email); + if (error) throw error; + + Alert.alert('Sucesso!', 'Verifique seu email para redefinir a palavra-passe'); + router.back(); // volta para login + } catch (err: any) { + Alert.alert('Erro', err.message); + } finally { + setLoading(false); + } + }; + + return ( + + + + + Recuperar Palavra-passe + Insira seu email para receber o link de redefinição + + {/* INPUT EMAIL */} + + + {/* BOTÃO ENVIAR LINK */} + + {loading ? : ENVIAR LINK} + + + {/* BOTÃO VOLTAR */} + router.push('/')} style={styles.backContainer}> + ← Voltar para Login + + + + + + ); +} + +const styles = StyleSheet.create({ + scrollContainer: { flexGrow: 1, justifyContent: 'center', padding: 24 }, + container: { + backgroundColor: '#fff', + borderRadius: 16, + padding: 24, + shadowColor: '#000', + shadowOffset: { width: 0, height: 6 }, + shadowOpacity: 0.1, + shadowRadius: 12, + elevation: 5, + }, + logo: { + width: 120, + height: 120, + alignSelf: 'center', + marginBottom: 20, + }, + title: { fontSize: 24, fontWeight: '700', color: '#2d3436', marginBottom: 8, textAlign: 'center' }, + subtitle: { fontSize: 14, color: '#636e72', marginBottom: 20, textAlign: 'center' }, + input: { + backgroundColor: '#f1f2f6', + borderRadius: 12, + paddingHorizontal: 16, + paddingVertical: 14, + fontSize: 16, + marginBottom: 20, + borderWidth: 0, + color: '#2d3436', + }, + button: { + backgroundColor: '#0984e3', + borderRadius: 12, + paddingVertical: 16, + alignItems: 'center', + marginBottom: 12, + shadowColor: '#0984e3', + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.3, + shadowRadius: 6, + elevation: 3, + }, + buttonDisabled: { backgroundColor: '#74b9ff' }, + buttonText: { color: '#fff', fontSize: 17, fontWeight: '700' }, + backContainer: { marginTop: 8, alignItems: 'center' }, + backText: { color: '#0984e3', fontSize: 15, fontWeight: '500' }, +}); diff --git a/app/register.tsx b/app/register.tsx deleted file mode 100644 index cefe1e0..0000000 --- a/app/register.tsx +++ /dev/null @@ -1,312 +0,0 @@ - - -// app/register.tsx - TELA DE CRIAR CONTA - - -import { Link } from 'expo-router'; -import { StatusBar } from 'expo-status-bar'; -import { useState } from 'react'; -import { - ActivityIndicator, - Alert, - KeyboardAvoidingView, - Platform, - ScrollView, - StyleSheet, - Text, - TextInput, - TouchableOpacity, - View -} from 'react-native'; -import { SafeAreaView } from 'react-native-safe-area-context'; - -export default function CriarContaScreen() { - const [form, setForm] = useState({ - nome: '', - email: '', - telefone: '', - password: '', - confirmarPassword: '' - }); - const [loading, setLoading] = useState(false); - - const handleChange = (field: string, value: string) => { - setForm(prev => ({ ...prev, [field]: value })); - }; - - const handleRegister = () => { - if (!form.nome.trim()) { - Alert.alert('Erro', 'Por favor, insira o seu nome'); - return; - } - - if (!form.email.includes('@')) { - Alert.alert('Erro', 'Por favor, insira um email válido'); - return; - } - - if (form.password.length < 6) { - Alert.alert('Erro', 'A senha deve ter pelo menos 6 caracteres'); - return; - } - - if (form.password !== form.confirmarPassword) { - Alert.alert('Erro', 'As senhas não coincidem'); - return; - } - - setLoading(true); - - setTimeout(() => { - setLoading(false); - Alert.alert( - 'Sucesso!', - `Conta criada para ${form.nome}`, - [{ text: 'OK' }] - ); - - setForm({ - nome: '', - email: '', - telefone: '', - password: '', - confirmarPassword: '' - }); - }, 1500); - }; - - return ( - - - - - - {/* BOTÃO VOLTAR ATRÁS */} - - - - - - - {/* CABEÇALHO */} - - Criar Nova Conta - - Preencha os dados abaixo para se registar - - - - {/* FORMULÁRIO */} - - {/* NOME COMPLETO */} - - Nome Completo - handleChange('nome', text)} - editable={!loading} - /> - - - {/* EMAIL */} - - Email - handleChange('email', text)} - keyboardType="email-address" - autoCapitalize="none" - editable={!loading} - /> - - - {/* Nº TELEMÓVEL */} - - Telefone - handleChange('telefone', text)} - keyboardType="phone-pad" - editable={!loading} - /> - - - {/* PALAVRA-PASSE */} - - Senha - handleChange('password', text)} - secureTextEntry - editable={!loading} - /> - - - {/* CONFIRMAR PALAVRA-PASSE */} - - Confirmar Senha - handleChange('confirmarPassword', text)} - secureTextEntry - editable={!loading} - /> - - - {/* BOTÃO CRIAR CONTA */} - - {loading ? ( - - ) : ( - CRIAR CONTA - )} - - - {/* AVISO */} - - - Ao criar uma conta, concorda com os nossos Termos de Serviço e Política de Privacidade. - - - - - - - ); -} - -// ESTILOS -const styles = StyleSheet.create({ - safeArea: { - flex: 1, - backgroundColor: '#FFFFFF', - }, - container: { - flex: 1, - }, - scrollContainer: { - flexGrow: 1, - padding: 20, - paddingTop: 20, - }, - backHeaderButton: { - position: 'absolute', - top: 0, - left: 10, - zIndex: 50, - width: 40, - height: 40, - borderRadius: 20, - backgroundColor: '#f0f0f0', - justifyContent: 'center', - alignItems: 'center', - }, - backHeaderText: { - fontSize: 24, - color: '#007AFF', - fontWeight: 'bold', - }, - header: { - alignItems: 'center', - marginTop: 50, - marginBottom: 40, - }, - title: { - fontSize: 28, - fontWeight: 'bold', - color: '#1a1a1a', - marginBottom: 8, - }, - subtitle: { - fontSize: 16, - color: '#666', - textAlign: 'center', - }, - formCard: { - backgroundColor: '#f8f9fa', - borderRadius: 16, - padding: 24, - marginBottom: 24, - borderWidth: 1, - borderColor: '#e9ecef', - }, - inputGroup: { - marginBottom: 20, - }, - label: { - fontSize: 14, - fontWeight: '600', - color: '#333', - marginBottom: 6, - marginLeft: 4, - }, - input: { - backgroundColor: '#FFFFFF', - borderRadius: 12, - paddingHorizontal: 16, - paddingVertical: 14, - fontSize: 16, - borderWidth: 1, - borderColor: '#ddd', - color: '#333', - }, - registerButton: { - backgroundColor: '#007AFF', - borderRadius: 12, - paddingVertical: 16, - alignItems: 'center', - marginTop: 10, - marginBottom: 20, - }, - buttonDisabled: { - backgroundColor: '#7bb8ff', - }, - registerButtonText: { - color: '#FFFFFF', - fontSize: 16, - fontWeight: 'bold', - }, - termsContainer: { - backgroundColor: '#e8f4ff', - borderRadius: 8, - padding: 12, - borderWidth: 1, - borderColor: '#cce5ff', - }, - termsText: { - fontSize: 13, - color: '#0066cc', - textAlign: 'center', - lineHeight: 18, - }, - backButton: { - paddingVertical: 14, - alignItems: 'center', - }, - backButtonText: { - color: '#007AFF', - fontSize: 16, - fontWeight: '500', - }, -}); diff --git a/assets/images/logo.png b/assets/images/logo.png new file mode 100644 index 0000000..e69de29 diff --git a/components/Auth.tsx b/components/Auth.tsx new file mode 100644 index 0000000..af6de07 --- /dev/null +++ b/components/Auth.tsx @@ -0,0 +1,101 @@ +import { useRouter } from 'expo-router'; // IMPORTAR ROUTER +import { useState } from 'react'; +import { ActivityIndicator, Alert, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native'; +import { supabase } from '../app/lib/supabase'; + +interface AuthProps { + onLoginSuccess?: () => void; +} + +export default function Auth({ onLoginSuccess }: AuthProps) { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [loading, setLoading] = useState(false); + const router = useRouter(); // INICIALIZA O ROUTER + + // LOGIN + const handleLogin = async () => { + if (!email || !password) { + Alert.alert('Atenção', 'Preencha todos os campos'); + return; + } + + setLoading(true); + try { + const { error } = await supabase.auth.signInWithPassword({ email, password }); + if (error) throw error; + + Alert.alert('Bem-vindo(a)!'); + if (onLoginSuccess) onLoginSuccess(); + } catch (err: any) { + Alert.alert('Erro', err.message); + } finally { + setLoading(false); + } + }; + + return ( + + {/* EMAIL */} + Email + + + {/* PASSWORD */} + Palavra-passe + + + {/* BOTÃO ENTRAR MODERNO */} + + {loading ? ( + + ) : ( + ENTRAR + )} + + + {/* TEXTO DE ESQUECI A SENHA → NAVEGA */} + router.push('/redefenirsenha')}> + Esqueceu a palavra-passe? + + + ); +} + +const styles = StyleSheet.create({ + form: { + backgroundColor: '#fff', + borderRadius: 16, + padding: 24, + marginTop: 20, + shadowColor: '#000', + shadowOffset: { width: 0, height: 6 }, + shadowOpacity: 0.1, + shadowRadius: 12, + elevation: 5, + }, + label: { fontSize: 14, fontWeight: '600', color: '#2d3436', marginBottom: 8 }, + input: { backgroundColor: '#f1f2f6', borderRadius: 12, paddingHorizontal: 16, paddingVertical: 14, fontSize: 16, marginBottom: 20, borderWidth: 0, color: '#2d3436' }, + button: { backgroundColor: '#0984e3', borderRadius: 12, paddingVertical: 16, alignItems: 'center', marginBottom: 12, shadowColor: '#0984e3', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.3, shadowRadius: 6, elevation: 3 }, + buttonDisabled: { backgroundColor: '#74b9ff' }, + buttonText: { color: '#fff', fontSize: 17, fontWeight: '700' }, + forgotText: { color: '#0984e3', fontSize: 15, fontWeight: '500', textAlign: 'center', marginTop: 8 }, +}); diff --git a/package-lock.json b/package-lock.json index b48875c..d34aaf4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@react-navigation/bottom-tabs": "^7.4.0", "@react-navigation/elements": "^2.6.3", "@react-navigation/native": "^7.1.8", + "@supabase/supabase-js": "^2.91.0", "expo": "~54.0.27", "expo-constants": "~18.0.11", "expo-document-picker": "~14.0.8", @@ -35,6 +36,7 @@ "react-native-reanimated": "~4.1.1", "react-native-safe-area-context": "~5.6.0", "react-native-screens": "~4.16.0", + "react-native-url-polyfill": "^3.0.0", "react-native-web": "~0.21.0", "react-native-worklets": "0.5.1" }, @@ -3463,6 +3465,107 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@supabase/auth-js": { + "version": "2.91.0", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.91.0.tgz", + "integrity": "sha512-9ywvsKLsxTwv7fvN5fXzP3UfRreqrX2waylTBDu0lkmeHXa8WtSQS9e0WV9FBduiazYqQbgfBQXBNPRPsRgWOQ==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.91.0", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.91.0.tgz", + "integrity": "sha512-WaakXOqLK1mLtBNFXp5o5T+LlI6KZuADSeXz+9ofPRG5OpVSvW148LVJB1DRZ16Phck1a0YqIUswOUgxCz6vMw==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/postgrest-js": { + "version": "2.91.0", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.91.0.tgz", + "integrity": "sha512-5S41zv2euNpGucvtM4Wy+xOmLznqt/XO+Lh823LOFEQ00ov7QJfvqb6VzIxufvzhooZpmGR0BxvMcJtWxCIFdQ==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.91.0", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.91.0.tgz", + "integrity": "sha512-u2YuJFG35umw8DO9beC27L/jYXm3KhF+73WQwbynMpV0tXsFIA0DOGRM0NgRyy03hJIdO6mxTTwe8efW3yx3Tg==", + "license": "MIT", + "dependencies": { + "@types/phoenix": "^1.6.6", + "@types/ws": "^8.18.1", + "tslib": "2.8.1", + "ws": "^8.18.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/realtime-js/node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.91.0", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.91.0.tgz", + "integrity": "sha512-CI7fsVIBQHfNObqU9kmyQ1GWr+Ug44y4rSpvxT4LdQB9tlhg1NTBov6z7Dlmt8d6lGi/8a9lf/epCDxyWI792g==", + "license": "MIT", + "dependencies": { + "iceberg-js": "^0.8.1", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.91.0", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.91.0.tgz", + "integrity": "sha512-Rjb0QqkKrmXMVwUOdEqysPBZ0ZDZakeptTkUa6k2d8r3strBdbWVDqjOdkCjAmvvZMtXecBeyTyMEXD1Zzjfvg==", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.91.0", + "@supabase/functions-js": "2.91.0", + "@supabase/postgrest-js": "2.91.0", + "@supabase/realtime-js": "2.91.0", + "@supabase/storage-js": "2.91.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", @@ -3584,6 +3687,12 @@ "undici-types": "~7.16.0" } }, + "node_modules/@types/phoenix": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.7.tgz", + "integrity": "sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==", + "license": "MIT" + }, "node_modules/@types/react": { "version": "19.1.17", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.17.tgz", @@ -3600,6 +3709,15 @@ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "license": "MIT" }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/yargs": { "version": "17.0.35", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", @@ -7709,6 +7827,15 @@ "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==", "license": "BSD-3-Clause" }, + "node_modules/iceberg-js": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", + "integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -10703,6 +10830,18 @@ "integrity": "sha512-Ns7Bn9H/Tyw278+5SQx9oAblDZ7JixyzeOczcBK8dipQk2pD7Djkcfnf1nB/8RErAmMLL9iXgW0QHqiII8AhKw==", "license": "MIT" }, + "node_modules/react-native-url-polyfill": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/react-native-url-polyfill/-/react-native-url-polyfill-3.0.0.tgz", + "integrity": "sha512-aA5CiuUCUb/lbrliVCJ6lZ17/RpNJzvTO/C7gC/YmDQhTUoRD5q5HlJfwLWcxz4VgAhHwXKzhxH+wUN24tAdqg==", + "license": "MIT", + "dependencies": { + "whatwg-url-without-unicode": "8.0.0-3" + }, + "peerDependencies": { + "react-native": "*" + } + }, "node_modules/react-native-web": { "version": "0.21.2", "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.21.2.tgz", @@ -11994,9 +12133,9 @@ } }, "node_modules/tar": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz", - "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.4.tgz", + "integrity": "sha512-AN04xbWGrSTDmVwlI4/GTlIIwMFk/XEv7uL8aa57zuvRy6s4hdBed+lVq2fAZ89XDa7Us3ANXcE3Tvqvja1kTA==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", diff --git a/package.json b/package.json index caf1c8b..f191b54 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@react-navigation/bottom-tabs": "^7.4.0", "@react-navigation/elements": "^2.6.3", "@react-navigation/native": "^7.1.8", + "@supabase/supabase-js": "^2.91.0", "expo": "~54.0.27", "expo-constants": "~18.0.11", "expo-document-picker": "~14.0.8", @@ -38,6 +39,7 @@ "react-native-reanimated": "~4.1.1", "react-native-safe-area-context": "~5.6.0", "react-native-screens": "~4.16.0", + "react-native-url-polyfill": "^3.0.0", "react-native-web": "~0.21.0", "react-native-worklets": "0.5.1" },