diff --git a/app.json b/app.json index 3483ad4..516223f 100644 --- a/app.json +++ b/app.json @@ -14,7 +14,8 @@ "backgroundColor": "#ffffff" }, "ios": { - "supportsTablet": true + "supportsTablet": true, + "bundleIdentifier": "com.epvc.roadtripdj" }, "android": { "package": "com.eduardo12345122.roadtripdj", diff --git a/screenshot_login.png b/screenshot_login.png new file mode 100644 index 0000000..43d0985 Binary files /dev/null and b/screenshot_login.png differ diff --git a/src/auth/spotifyToken.ts b/src/auth/spotifyToken.ts index 5da424d..e3735e3 100644 --- a/src/auth/spotifyToken.ts +++ b/src/auth/spotifyToken.ts @@ -45,3 +45,52 @@ export async function clearSpotifyTokens() { console.error('Error clearing Spotify tokens:', error); } } + +export async function refreshSpotifyToken() { + const refreshToken = await getSpotifyRefreshToken(); + if (!refreshToken) { + console.log('[SpotifyToken] No refresh token found, cannot refresh.'); + return null; + } + + try { + console.log('[SpotifyToken] Refreshing Spotify access token...'); + const SPOTIFY_CLIENT_ID = "7fa1e7acbf7e44f18bf28d74f14fb9cb"; + + // Construct urlencoded body safely + const bodyDetails = { + grant_type: 'refresh_token', + refresh_token: refreshToken, + client_id: SPOTIFY_CLIENT_ID, + }; + const formBody = Object.keys(bodyDetails) + .map(key => encodeURIComponent(key) + '=' + encodeURIComponent(bodyDetails[key as keyof typeof bodyDetails])) + .join('&'); + + const res = await fetch('https://accounts.spotify.com/api/token', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: formBody, + }); + + if (res.ok) { + const data = await res.json(); + if (data.access_token) { + console.log('[SpotifyToken] Spotify access token refreshed successfully!'); + await setSpotifyToken(data.access_token); + if (data.refresh_token) { + await setSpotifyRefreshToken(data.refresh_token); + } + return data.access_token; + } + } else { + const errText = await res.text(); + console.error('[SpotifyToken] Refresh request failed:', res.status, errText); + } + } catch (error) { + console.error('[SpotifyToken] Error refreshing Spotify token:', error); + } + return null; +} diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx index fa42e6f..4be467a 100644 --- a/src/contexts/AuthContext.tsx +++ b/src/contexts/AuthContext.tsx @@ -48,12 +48,28 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { supabase.auth.getSession().then(({ data: { session } }) => { setSession(session); setUser(session?.user ?? null); + if (!session) { + setIsDemoMode(false); + setIsSpotifyAuthenticated(false); + } else { + const isSpotify = !!session.user?.user_metadata?.spotify_id; + setIsSpotifyAuthenticated(isSpotify); + setIsDemoMode(false); + } setLoading(false); }); const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => { setSession(session); setUser(session?.user ?? null); + if (!session) { + setIsDemoMode(false); + setIsSpotifyAuthenticated(false); + } else { + const isSpotify = !!session.user?.user_metadata?.spotify_id; + setIsSpotifyAuthenticated(isSpotify); + setIsDemoMode(false); + } setLoading(false); }); diff --git a/src/screens/auth/LoginScreen.tsx b/src/screens/auth/LoginScreen.tsx index ad99ee9..8e1c417 100644 --- a/src/screens/auth/LoginScreen.tsx +++ b/src/screens/auth/LoginScreen.tsx @@ -4,7 +4,6 @@ import { Car, Music } from 'lucide-react-native'; import { colors } from '../../utils/colors'; import { supabase } from '../../services/supabase'; import * as WebBrowser from 'expo-web-browser'; -import * as Linking from 'expo-linking'; import { makeRedirectUri, useAuthRequest, exchangeCodeAsync, DiscoveryDocument } from 'expo-auth-session'; import { clearSpotifyTokens, setSpotifyToken, setSpotifyRefreshToken } from '../../auth/spotifyToken'; import { useAuth } from '../../contexts/AuthContext'; @@ -24,11 +23,14 @@ export default function LoginScreen({ navigation }) { const [password, setPassword] = useState(''); const [loading, setLoading] = useState(false); - // Temporary Emergency Fix: Hardcoded Client ID + // Direct Spotify App Client ID const SPOTIFY_CLIENT_ID = "7fa1e7acbf7e44f18bf28d74f14fb9cb"; - // Configure Direct Spotify OAuth - const redirectUri = "exp://192.168.1.7:8081/--/auth/callback"; + // Configure Dynamic Redirect URI + const redirectUri = makeRedirectUri({ + scheme: 'roadtripdj', + path: 'auth/callback', + }); const [request, response, promptAsync] = useAuthRequest( { @@ -79,69 +81,88 @@ export default function LoginScreen({ navigation }) { await setSpotifyRefreshToken(tokenResult.refreshToken); } - // 1 & 2. Check for Supabase session or create an anonymous one - const { data: sessionData } = await supabase.auth.getSession(); - let supabaseUserId = sessionData?.session?.user?.id; + // Fetch real Spotify Profile + console.log("SPOTIFY_PROFILE_FETCH_START"); + const profileRes = await fetch('https://api.spotify.com/v1/me', { + headers: { Authorization: `Bearer ${tokenResult.accessToken}` } + }); - console.log("SUPABASE_SESSION_EXISTS:", !!sessionData?.session); - - if (!sessionData?.session) { - const { data, error } = await supabase.auth.signInAnonymously(); - if (error) { - console.error("Error creating anonymous session:", error); - throw error; - } - supabaseUserId = data.user?.id; + if (!profileRes.ok) { + throw new Error(`Failed to fetch Spotify profile: ${profileRes.statusText}`); } + + const profile = await profileRes.json(); + console.log("SPOTIFY_PROFILE_NAME:", profile.display_name); + console.log("SPOTIFY_PROFILE_EMAIL_EXISTS:", !!profile.email); + console.log("SPOTIFY_USER_ID:", profile.id); + console.log("SPOTIFY_ACCESS_TOKEN_EXISTS:", !!tokenResult.accessToken); - console.log("SUPABASE_USER_ID:", supabaseUserId); - console.log("TRIP_INSERT_USER_ID:", supabaseUserId); + const email = profile.email || `spotify_${profile.id}@roadtripdj.local`; + const password = `SpotifySecure_${profile.id}`; - // 7 & 8. Fetch Spotify Profile and update Supabase User Metadata - try { - console.log("SPOTIFY_PROFILE_FETCH_START"); - const profileRes = await fetch('https://api.spotify.com/v1/me', { - headers: { Authorization: `Bearer ${tokenResult.accessToken}` } - }); - - if (profileRes.ok) { - const profile = await profileRes.json(); - console.log("SPOTIFY_PROFILE_NAME:", profile.display_name); - console.log("SPOTIFY_PROFILE_EMAIL_EXISTS:", !!profile.email); - console.log("SPOTIFY_USER_ID:", profile.id); - console.log("SPOTIFY_ACCESS_TOKEN_EXISTS:", !!tokenResult.accessToken); - - // Force session refresh to ensure AuthContext picks up the new metadata instantly - const { data: updatedUser } = await supabase.auth.updateUser({ - data: { - display_name: profile.display_name, - name: profile.display_name, - spotify_id: profile.id, - email: profile.email, - avatar_url: profile.images?.[0]?.url - } - }); - - // Explicitly refresh session if needed - if (updatedUser) { - await supabase.auth.refreshSession(); + // Attempt sign in or automatic registration + let { data: sessionData, error: signInError } = await supabase.auth.signInWithPassword({ + email, + password, + }); + + if (signInError) { + console.log("Spotify user does not exist or login failed, signing up...", signInError.message); + + // Sign up user with Spotify metadata + const { error: signUpError } = await supabase.auth.signUp({ + email, + password, + options: { + data: { + display_name: profile.display_name || 'Viajante', + name: profile.display_name || 'Viajante', + spotify_id: profile.id, + email: profile.email || null, + avatar_url: profile.images?.[0]?.url || null, } - } - } catch (profileError) { - console.error("Error fetching Spotify profile:", profileError); + } + }); + + if (signUpError) { + console.error("SignUp error:", signUpError); + throw signUpError; + } + + // Log in again after sign up + const { data: newSession, error: newSignInError } = await supabase.auth.signInWithPassword({ + email, + password, + }); + if (newSignInError) throw newSignInError; + sessionData = newSession; + } else { + // Update metadata with latest Spotify data + await supabase.auth.updateUser({ + data: { + display_name: profile.display_name || 'Viajante', + name: profile.display_name || 'Viajante', + spotify_id: profile.id, + email: profile.email || null, + avatar_url: profile.images?.[0]?.url || null, + } + }); } + + // Force session refresh for app state + await supabase.auth.refreshSession(); console.log("LOGIN_MODE:", "spotify"); console.log("ENTERING_SPOTIFY_MODE"); - // Trigger app main flow as Spotify authenticated user + // Trigger app main flow enableSpotifyMode(); } else { Alert.alert('Erro de Autenticação', 'Não foi possível obter o token de acesso do Spotify.'); } } catch (e: any) { console.error('🚀 [LoginScreen] Token Exchange Error:', e); - Alert.alert('Erro de Autenticação', 'Não foi possível trocar o código pelo token.'); + Alert.alert('Erro de Autenticação', e.message || 'Não foi possível trocar o código pelo token.'); } finally { setLoading(false); } @@ -165,10 +186,6 @@ export default function LoginScreen({ navigation }) { setLoading(false); }; - const handleAuthDebug = async () => { - // Placeholder function for debug logic - }; - const handleResetAuth = async () => { await supabase.auth.signOut(); await clearSpotifyTokens(); @@ -178,23 +195,7 @@ export default function LoginScreen({ navigation }) { const handleSpotifyLogin = async () => { try { console.log("SPOTIFY_CLIENT_ID_EXISTS:", !!SPOTIFY_CLIENT_ID); - console.log("SPOTIFY_CLIENT_ID_LENGTH:", SPOTIFY_CLIENT_ID.length); - console.log("SPOTIFY_CLIENT_ID_FIRST_6:", SPOTIFY_CLIENT_ID.slice(0, 6)); console.log("SPOTIFY_REDIRECT_URI:", redirectUri); - console.log("FULL_SPOTIFY_AUTH_URL:", request?.url); - - if ( - !SPOTIFY_CLIENT_ID || - SPOTIFY_CLIENT_ID.includes("PASTE_") || - SPOTIFY_CLIENT_ID.includes("HERE") - ) { - Alert.alert( - 'Aviso', - 'Cole o Client ID real no arquivo LoginScreen.tsx.' - ); - return; - } - await promptAsync(); } catch (e: any) { console.error('🚀 [LoginScreen] OAuth Error:', e); @@ -205,10 +206,13 @@ export default function LoginScreen({ navigation }) { return ( - + {/* Header Section */} @@ -265,9 +269,6 @@ export default function LoginScreen({ navigation }) { style={[styles.primaryButton, { backgroundColor: '#333', marginBottom: 24 }]} onPress={() => { console.log("DEMO_BYPASS_PRESSED"); - console.log("LOGIN_MODE:", "demo"); - console.log("ENTERING_DEMO_MODE"); - console.log("AVAILABLE_ROUTE_NAMES", navigation.getState()?.routeNames); enableDemoMode(); }} > @@ -275,12 +276,8 @@ export default function LoginScreen({ navigation }) { )} - - Auth Debug - - - Reset Auth + Reset Auth @@ -306,15 +303,13 @@ const styles = StyleSheet.create({ }, scrollContent: { flexGrow: 1, - justifyContent: 'center', - alignItems: 'center', - paddingHorizontal: 20, + justifyContent: 'space-between', paddingTop: 60, - paddingBottom: 40, }, headerContainer: { alignItems: 'center', - marginBottom: 40, + marginBottom: 30, + paddingHorizontal: 20, }, iconWrapper: { position: 'relative', @@ -357,10 +352,16 @@ const styles = StyleSheet.create({ card: { backgroundColor: colors.white, width: '100%', - borderRadius: 24, + borderTopLeftRadius: 32, + borderTopRightRadius: 32, + borderBottomLeftRadius: 0, + borderBottomRightRadius: 0, padding: 24, + paddingBottom: Platform.OS === 'ios' ? 40 : 24, + flexGrow: 1, + justifyContent: 'center', shadowColor: '#000', - shadowOffset: { width: 0, height: 4 }, + shadowOffset: { width: 0, height: -4 }, shadowOpacity: 0.1, shadowRadius: 12, elevation: 5, @@ -407,6 +408,7 @@ const styles = StyleSheet.create({ flexDirection: 'row', justifyContent: 'center', alignItems: 'center', + marginTop: 10, }, footerText: { color: colors.textSecondary, diff --git a/src/screens/auth/RegisterScreen.tsx b/src/screens/auth/RegisterScreen.tsx index 41207e4..93f8b0c 100644 --- a/src/screens/auth/RegisterScreen.tsx +++ b/src/screens/auth/RegisterScreen.tsx @@ -1,21 +1,171 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { View, Text, TextInput, TouchableOpacity, StyleSheet, SafeAreaView, KeyboardAvoidingView, Platform, ScrollView, Alert, ActivityIndicator } from 'react-native'; import { Car, Music } from 'lucide-react-native'; import { colors } from '../../utils/colors'; import { supabase } from '../../services/supabase'; -import { makeRedirectUri } from 'expo-auth-session'; import * as WebBrowser from 'expo-web-browser'; -import * as QueryParams from 'expo-auth-session/build/QueryParams'; +import { makeRedirectUri, useAuthRequest, exchangeCodeAsync, DiscoveryDocument } from 'expo-auth-session'; +import { clearSpotifyTokens, setSpotifyToken, setSpotifyRefreshToken } from '../../auth/spotifyToken'; +import { useAuth } from '../../contexts/AuthContext'; WebBrowser.maybeCompleteAuthSession(); + +// Direct Spotify OAuth Endpoints +const discovery: DiscoveryDocument = { + authorizationEndpoint: 'https://accounts.spotify.com/authorize', + tokenEndpoint: 'https://accounts.spotify.com/api/token', +}; + // @ts-ignore export default function RegisterScreen({ navigation }) { + const { enableSpotifyMode } = useAuth(); const [name, setName] = useState(''); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [loading, setLoading] = useState(false); + // Direct Spotify App Client ID + const SPOTIFY_CLIENT_ID = "7fa1e7acbf7e44f18bf28d74f14fb9cb"; + + // Configure Dynamic Redirect URI + const redirectUri = makeRedirectUri({ + scheme: 'roadtripdj', + path: 'auth/callback', + }); + + const [request, response, promptAsync] = useAuthRequest( + { + clientId: SPOTIFY_CLIENT_ID, + scopes: ['user-read-email', 'user-read-private', 'playlist-modify-public', 'playlist-modify-private'], + usePKCE: true, + redirectUri, + }, + discovery + ); + + // Handle Spotify OAuth Response + useEffect(() => { + if (response) { + console.log("SPOTIFY_AUTH_RESULT (Register):", response); + if (response.type === 'success') { + const { code } = response.params; + console.log("SPOTIFY_CODE_EXISTS (Register):", !!code); + exchangeCodeForTokens(code); + } else if (response.type === 'error') { + Alert.alert('Erro de Autenticação', response.error?.message || 'Falha ao logar com o Spotify'); + } + } + }, [response]); + + const exchangeCodeForTokens = async (code: string) => { + if (!request?.codeVerifier) return; + + try { + setLoading(true); + const tokenResult = await exchangeCodeAsync( + { + clientId: SPOTIFY_CLIENT_ID, + code, + redirectUri, + extraParams: { + code_verifier: request.codeVerifier, + }, + }, + discovery + ); + + console.log("SPOTIFY_TOKEN_RECEIVED (Register):", !!tokenResult.accessToken); + + if (tokenResult.accessToken) { + await setSpotifyToken(tokenResult.accessToken); + if (tokenResult.refreshToken) { + await setSpotifyRefreshToken(tokenResult.refreshToken); + } + + // Fetch Spotify Profile + console.log("SPOTIFY_PROFILE_FETCH_START (Register)"); + const profileRes = await fetch('https://api.spotify.com/v1/me', { + headers: { Authorization: `Bearer ${tokenResult.accessToken}` } + }); + + if (!profileRes.ok) { + throw new Error(`Failed to fetch Spotify profile: ${profileRes.statusText}`); + } + + const profile = await profileRes.json(); + console.log("SPOTIFY_PROFILE_NAME (Register):", profile.display_name); + console.log("SPOTIFY_PROFILE_EMAIL_EXISTS (Register):", !!profile.email); + console.log("SPOTIFY_USER_ID (Register):", profile.id); + + const email = profile.email || `spotify_${profile.id}@roadtripdj.local`; + const password = `SpotifySecure_${profile.id}`; + + // Attempt sign in or automatic registration + let { data: sessionData, error: signInError } = await supabase.auth.signInWithPassword({ + email, + password, + }); + + if (signInError) { + console.log("Spotify user does not exist or login failed, signing up (Register)...", signInError.message); + + // Sign up user with Spotify metadata + const { error: signUpError } = await supabase.auth.signUp({ + email, + password, + options: { + data: { + display_name: profile.display_name || 'Viajante', + name: profile.display_name || 'Viajante', + spotify_id: profile.id, + email: profile.email || null, + avatar_url: profile.images?.[0]?.url || null, + } + } + }); + + if (signUpError) { + console.error("SignUp error:", signUpError); + throw signUpError; + } + + // Log in again after sign up + const { data: newSession, error: newSignInError } = await supabase.auth.signInWithPassword({ + email, + password, + }); + if (newSignInError) throw newSignInError; + sessionData = newSession; + } else { + // Update metadata with latest Spotify data + await supabase.auth.updateUser({ + data: { + display_name: profile.display_name || 'Viajante', + name: profile.display_name || 'Viajante', + spotify_id: profile.id, + email: profile.email || null, + avatar_url: profile.images?.[0]?.url || null, + } + }); + } + + // Force session refresh for app state + await supabase.auth.refreshSession(); + + // Trigger app main flow + enableSpotifyMode(); + } else { + Alert.alert('Erro de Autenticação', 'Não foi possível obter o token de acesso do Spotify.'); + } + } catch (e: any) { + console.error('🚀 [RegisterScreen] Token Exchange Error:', e); + Alert.alert('Erro de Autenticação', e.message || 'Não foi possível trocar o código pelo token.'); + } finally { + setLoading(false); + } + }; + const handleRegister = async () => { if (!name || !email || !password || !confirmPassword) { Alert.alert('Erro', 'Por favor preenche todos os campos.'); @@ -39,48 +189,34 @@ export default function RegisterScreen({ navigation }) { if (error) { Alert.alert('Erro no registo', error.message); + } else { + Alert.alert('Sucesso', 'Conta criada com sucesso! Faça login.'); + navigation.navigate('Login'); } setLoading(false); }; const handleSpotifyAuth = async () => { try { - const redirectUri = makeRedirectUri(); - const { data, error } = await supabase.auth.signInWithOAuth({ - provider: 'spotify', - options: { - redirectTo: redirectUri, - }, - }); - - if (error) throw error; - - if (data?.url) { - const res = await WebBrowser.openAuthSessionAsync(data.url, redirectUri); - if (res.type === 'success') { - const { url } = res; - const { params, errorCode } = QueryParams.getQueryParams(url); - if (errorCode) throw new Error(errorCode); - if (params.access_token && params.refresh_token) { - await supabase.auth.setSession({ - access_token: params.access_token, - refresh_token: params.refresh_token, - }); - } - } - } - } catch (err: any) { - Alert.alert('Erro', err?.message || 'Ocorreu um erro no Spotify Auth.'); + console.log("SPOTIFY_CLIENT_ID_EXISTS (Register):", !!SPOTIFY_CLIENT_ID); + console.log("SPOTIFY_REDIRECT_URI (Register):", redirectUri); + await promptAsync(); + } catch (e: any) { + console.error('🚀 [RegisterScreen] OAuth Error:', e); + Alert.alert('Erro de Autenticação', e.message); } }; return ( - + {/* Header Section */} @@ -144,9 +280,8 @@ export default function RegisterScreen({ navigation }) { - {/* Note: Placeholder Spotify logo */} - Registar com Spotify + Criar conta com Spotify @@ -172,15 +307,13 @@ const styles = StyleSheet.create({ }, scrollContent: { flexGrow: 1, - justifyContent: 'center', - alignItems: 'center', - paddingHorizontal: 20, - paddingTop: 40, - paddingBottom: 40, + justifyContent: 'space-between', + paddingTop: 60, }, headerContainer: { alignItems: 'center', marginBottom: 30, + paddingHorizontal: 20, }, iconWrapper: { position: 'relative', @@ -223,10 +356,16 @@ const styles = StyleSheet.create({ card: { backgroundColor: colors.white, width: '100%', - borderRadius: 24, + borderTopLeftRadius: 32, + borderTopRightRadius: 32, + borderBottomLeftRadius: 0, + borderBottomRightRadius: 0, padding: 24, + paddingBottom: Platform.OS === 'ios' ? 40 : 24, + flexGrow: 1, + justifyContent: 'center', shadowColor: '#000', - shadowOffset: { width: 0, height: 4 }, + shadowOffset: { width: 0, height: -4 }, shadowOpacity: 0.1, shadowRadius: 12, elevation: 5, @@ -273,6 +412,7 @@ const styles = StyleSheet.create({ flexDirection: 'row', justifyContent: 'center', alignItems: 'center', + marginTop: 10, }, footerText: { color: colors.textSecondary, diff --git a/src/screens/trip/NewTripScreen.tsx b/src/screens/trip/NewTripScreen.tsx index c85dc7e..a0f1fb1 100644 --- a/src/screens/trip/NewTripScreen.tsx +++ b/src/screens/trip/NewTripScreen.tsx @@ -4,7 +4,8 @@ import { SafeAreaView } from 'react-native-safe-area-context'; import { X, MapPin, ArrowRight, Navigation } from 'lucide-react-native'; import { colors } from '../../utils/colors'; import { supabase } from '../../services/supabase'; -import { getSpotifyAccessToken } from '../../auth/spotifyToken'; +import { getSpotifyAccessToken, refreshSpotifyToken } from '../../auth/spotifyToken'; +import { OLLAMA_API_URL } from '../../services/ollama'; // @ts-ignore export default function NewTripScreen({ navigation }) { @@ -56,20 +57,52 @@ export default function NewTripScreen({ navigation }) { console.log(`PLAYLIST_API_CONTENT_TYPE [${label}]:`, res.headers.get("content-type")); console.log(`PLAYLIST_API_RAW_RESPONSE [${label}]:`, rawText.substring(0, 300) + (rawText.length > 300 ? "..." : "")); + if (!res.ok) { + throw new Error(`Spotify API returned status ${res.status} for [${label}]: ${rawText.substring(0, 150)}`); + } + + const contentType = res.headers.get("content-type") || ""; + if (!contentType.includes("application/json")) { + throw new Error(`Playlist API returned non-JSON response for [${label}]: ${rawText.substring(0, 150)}`); + } + try { return JSON.parse(rawText); } catch (e) { - throw new Error(`Playlist API returned non-JSON response [${label}]: ${rawText}`); + throw new Error(`Failed to parse JSON response for [${label}]: ${rawText.substring(0, 150)}`); } }; // A. Get provider token - const providerToken = await getSpotifyAccessToken(); + let providerToken = await getSpotifyAccessToken(); console.log("SPOTIFY_ACCESS_TOKEN_EXISTS:", !!providerToken); + + if (providerToken) { + // Proactively check if token is valid, or refresh it + console.log("Validating Spotify token..."); + let testRes = await fetch('https://api.spotify.com/v1/me', { + headers: { Authorization: `Bearer ${providerToken}` } + }); + + if (testRes.status === 401) { + console.log("Spotify token is invalid/expired (401), attempting to refresh..."); + const newToken = await refreshSpotifyToken(); + if (newToken) { + providerToken = newToken; + } else { + console.log("Failed to refresh Spotify token."); + providerToken = null; + } + } else if (!testRes.ok) { + const testErr = await testRes.text(); + console.error("Spotify validation request failed:", testRes.status, testErr); + providerToken = null; + } + } if (!providerToken) { - console.log("Spotify token missing, skipping playlist generation."); - Alert.alert('Aviso', 'Spotify token missing, please login again'); + console.log("Spotify token missing or expired, skipping playlist generation."); + Alert.alert('Spotify Desligado', 'O token do Spotify expirou ou está em falta. Por favor reconecte o Spotify no Perfil.'); } else { // B. Fetch Spotify User ID const spotifyUserRes = await fetch('https://api.spotify.com/v1/me', { @@ -83,7 +116,7 @@ export default function NewTripScreen({ navigation }) { const spotifyUserId = spotifyUserData.id; // C. Call Ollama server - const ollamaRes = await fetch("http://89.114.196.110:11434/api/chat", { + const ollamaRes = await fetch(`${OLLAMA_API_URL}/api/chat`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ diff --git a/src/services/ollama.ts b/src/services/ollama.ts index d6e8e77..95d3fa5 100644 --- a/src/services/ollama.ts +++ b/src/services/ollama.ts @@ -1,5 +1,5 @@ // Placeholder for Ollama API logic -export const OLLAMA_API_URL = "https://apichat.epvc.pt/"; +export const OLLAMA_API_URL = "https://apichat.epvc.pt"; export const generateTripGuide = async (origin: string, destination: string, waypoints: string[], duration: string) => { // Logic to call Ollama