From 632aed8181407d8399431e4baf2441fecb35eec3 Mon Sep 17 00:00:00 2001
From: Ricardo Gomes <230413@epvc.pt>
Date: Tue, 20 Jan 2026 17:15:23 +0000
Subject: [PATCH] 26.1.20
---
app/index.tsx | 151 +++-----------------
app/lib/supabase.ts | 32 +++++
app/redefenirsenha.tsx | 131 +++++++++++++++++
app/register.tsx | 312 -----------------------------------------
assets/images/logo.png | 0
components/Auth.tsx | 101 +++++++++++++
package-lock.json | 145 ++++++++++++++++++-
package.json | 2 +
8 files changed, 429 insertions(+), 445 deletions(-)
create mode 100644 app/lib/supabase.ts
create mode 100644 app/redefenirsenha.tsx
delete mode 100644 app/register.tsx
create mode 100644 assets/images/logo.png
create mode 100644 components/Auth.tsx
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"
},