From 8dc51fafdf9982055012070302c43ae36be2ed09 Mon Sep 17 00:00:00 2001 From: Andrii Solianyk Date: Wed, 4 Jun 2025 22:09:03 +0200 Subject: [PATCH 1/3] AUTORYZACJA DO GOOOOOGLA DZIALA --- ArtisanConnect/app.json | 18 ++++++++-- ArtisanConnect/app/(tabs)/login.jsx | 55 +++++++++++++++++++++++++++-- ArtisanConnect/eas.json | 21 +++++++++++ ArtisanConnect/package.json | 5 +-- 4 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 ArtisanConnect/eas.json diff --git a/ArtisanConnect/app.json b/ArtisanConnect/app.json index 71e8089..ba2a2c7 100644 --- a/ArtisanConnect/app.json +++ b/ArtisanConnect/app.json @@ -14,13 +14,19 @@ "backgroundColor": "#ffffff" }, "ios": { - "supportsTablet": true + "supportsTablet": true, + "bundleIdentifier": "com.hamx.ArtisanConnect" }, "android": { "adaptiveIcon": { "foregroundImage": "./assets/adaptive-icon.png", "backgroundColor": "#ffffff" - } + }, + "permissions": [ + "android.permission.RECORD_AUDIO", + "android.permission.CAMERA" + ], + "package": "com.hamx.ArtisanConnect" }, "web": { "favicon": "./assets/favicon.png" @@ -40,6 +46,12 @@ } ], "expo-web-browser" - ] + ], + "extra": { + "router": {}, + "eas": { + "projectId": "7a0d8bc8-938f-4d2a-babb-945faee13429" + } + } } } diff --git a/ArtisanConnect/app/(tabs)/login.jsx b/ArtisanConnect/app/(tabs)/login.jsx index 0fbbf82..c83452d 100644 --- a/ArtisanConnect/app/(tabs)/login.jsx +++ b/ArtisanConnect/app/(tabs)/login.jsx @@ -1,4 +1,4 @@ -import React, {useState} from 'react'; +import React, {useEffect, useState} from 'react'; import {StyleSheet, ActivityIndicator, SafeAreaView, View} from 'react-native'; import {useAuthStore} from '@/store/authStore'; import {useRouter, Link} from 'expo-router'; @@ -15,12 +15,27 @@ import {ArrowRightIcon} from "@/components/ui/icon" import {Divider} from '@/components/ui/divider'; import {Ionicons} from "@expo/vector-icons"; +import * as WebBrowser from 'expo-web-browser'; +import * as Google from "expo-auth-session/providers/google"; +import AsyncStorage from "@react-native-async-storage/async-storage"; + +WebBrowser.maybeCompleteAuthSession(); + +// client_id ios 936418008320-ohefdfcebd41f6oa2o8phh1mgj9s49sl.apps.googleusercontent.com +// android 936418008320-d8dfjph5e4r28fcm1rbdfbh5phmbg03d.apps.googleusercontent.com + export default function Login() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const {signIn, isLoading} = useAuthStore(); const router = useRouter(); + const [request, response, promptAsync] = Google.useAuthRequest({ + androidClientId: "936418008320-d8dfjph5e4r28fcm1rbdfbh5phmbg03d.apps.googleusercontent.com", + iosClientId: "936418008320-ohefdfcebd41f6oa2o8phh1mgj9s49sl.apps.googleusercontent.com", + webClientId: "936418008320-btdngtlfnjac1p67guje72m9el5q59a7.apps.googleusercontent.com" + }) + const handleInternalLogin = async () => { if (!email || !password) { alert('Proszę wprowadzić email i hasło.'); @@ -36,6 +51,42 @@ export default function Login() { } } + useEffect(() => { + handleGoogleLogin(); + }, [response]); + + const handleGoogleLogin = async () => { + const user = await AsyncStorage.getItem("@user"); + if (!user) { + if(response?.type === "success") { + await getUserInfo(response.authentication.accessToken) + } + } else { + // Set the user in the auth store + } + }; + + const getUserInfo = async (token) => { + if(!token) { + return + } + try { + const response = await fetch("https://www.googleapis.com/userinfo/v2/me", + { + headers: { + Authorization: `Bearer ${token}` + }, + } + ); + const user = await response.json(); + console.log("Pobrano informacje o użytkowniku:", user); + await AsyncStorage.setItem("@user", JSON.stringify(user)); + } catch (error) { + console.error("Błąd podczas pobierania informacji o użytkowniku:", error); + throw error; + } + } + if (isLoading) { return ( @@ -82,7 +133,7 @@ export default function Login() { - diff --git a/ArtisanConnect/eas.json b/ArtisanConnect/eas.json new file mode 100644 index 0000000..a614f19 --- /dev/null +++ b/ArtisanConnect/eas.json @@ -0,0 +1,21 @@ +{ + "cli": { + "version": ">= 16.8.0", + "appVersionSource": "remote" + }, + "build": { + "development": { + "developmentClient": true, + "distribution": "internal" + }, + "preview": { + "distribution": "internal" + }, + "production": { + "autoIncrement": true + } + }, + "submit": { + "production": {} + } +} diff --git a/ArtisanConnect/package.json b/ArtisanConnect/package.json index f44ffd7..72d3615 100644 --- a/ArtisanConnect/package.json +++ b/ArtisanConnect/package.json @@ -53,11 +53,12 @@ "react-native-gesture-handler": "~2.24.0", "react-native-reanimated": "~3.17.4", "react-native-safe-area-context": "5.4.0", - "react-native-screens": "~4.10.0", + "react-native-screens": "~4.11.1", "react-native-svg": "15.11.2", "react-native-web": "~0.20.0", "tailwindcss": "^3.4.17", - "zustand": "^5.0.3" + "zustand": "^5.0.3", + "expo-crypto": "~14.1.4" }, "devDependencies": { "@babel/core": "^7.20.0", From b6f2225148e47d70f45b12258379730a1779b510 Mon Sep 17 00:00:00 2001 From: Andrii Solianyk Date: Wed, 4 Jun 2025 22:25:31 +0200 Subject: [PATCH 2/3] scheme fix --- ArtisanConnect/app.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ArtisanConnect/app.json b/ArtisanConnect/app.json index ba2a2c7..fab36c1 100644 --- a/ArtisanConnect/app.json +++ b/ArtisanConnect/app.json @@ -2,7 +2,7 @@ "expo": { "name": "ArtisanConnect", "slug": "ArtisanConnect", - "scheme": "Artisanconnect", + "scheme": "ArtisanConnect", "version": "1.0.0", "orientation": "portrait", "icon": "./assets/icon.png", From d4133f28bdf80cc54ca68c5ad5599434fca192af Mon Sep 17 00:00:00 2001 From: Andrii Solianyk Date: Fri, 6 Jun 2025 15:49:08 +0200 Subject: [PATCH 3/3] =?UTF-8?q?autoryzacja=20dzia=C5=82a=20zar=C3=B3wno=20?= =?UTF-8?q?na=20android=20jak=20i=20ios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ArtisanConnect/api/categories.jsx | 2 +- ArtisanConnect/api/notices.jsx | 21 ++++++---------- ArtisanConnect/app.json | 6 ++--- ArtisanConnect/app/(tabs)/login.jsx | 31 +++++++++++++++++------ ArtisanConnect/package.json | 2 +- ArtisanConnect/store/authStore.jsx | 39 +++++++++++++---------------- 6 files changed, 53 insertions(+), 48 deletions(-) diff --git a/ArtisanConnect/api/categories.jsx b/ArtisanConnect/api/categories.jsx index ae3d001..0b9bd47 100644 --- a/ArtisanConnect/api/categories.jsx +++ b/ArtisanConnect/api/categories.jsx @@ -1,6 +1,6 @@ import axios from "axios"; -const API_URL = "https://testowe.zikor.pl/api/v1"; +const API_URL = "https://hopp.zikor.pl/api/v1"; export async function listCategories() { try { diff --git a/ArtisanConnect/api/notices.jsx b/ArtisanConnect/api/notices.jsx index 42776df..ee84069 100644 --- a/ArtisanConnect/api/notices.jsx +++ b/ArtisanConnect/api/notices.jsx @@ -4,18 +4,15 @@ import {useAuthStore} from "@/store/authStore"; // const API_URL = "https://testowe.zikor.pl/api/v1"; -const API_URL = "http://10.0.2.2:8080/api/v1"; +const API_URL = "https://hopp.zikor.pl/api/v1"; export async function listNotices() { const { token } = useAuthStore.getState(); const headers = token ? { 'Authorization': `Bearer ${token}` } : {}; - console.log(token); - const response = await fetch(`${API_URL}/notices/get/all`, { headers: headers }); - console.log(response); const data = await response.json(); if (!response.ok) { throw new Error(response.toString()); @@ -62,8 +59,6 @@ export async function getImageByNoticeId(noticeId) { const imageName = listResponse.data[0]; imageUrl = `${API_URL}/images/get/${imageName}`; - console.log(`Pobrano zdjęcie o nazwie: ${imageName}`); - return imageUrl; } catch (err) { console.log(`Zdjęcie nie istnieje dla notice o id: ${noticeId}`); @@ -77,25 +72,23 @@ export async function getAllImagesByNoticeId(noticeId) { const listResponse = await axios.get(`${API_URL}/images/list/${noticeId}`); if (listResponse.data && listResponse.data.length > 0) { - const imageUrls = listResponse.data.map(imageName => + return listResponse.data.map(imageName => `${API_URL}/images/get/${imageName}` ); - - console.log(`Pobrano ${imageUrls.length} zdjęć dla ogłoszenia o id: ${noticeId}`); - return imageUrls; } - console.log(`Brak zdjęć dla ogłoszenia o id: ${noticeId}`); return ["https://http.cat/404.jpg"]; } catch (err) { - console.log(`Błąd podczas pobierania listy zdjęć dla ogłoszenia o id: ${noticeId}`, err); + if(err.response.status === 404) { + console.info(`Ogłoszenie o id: ${noticeId} nie posiada zdjęć.`); + return ["https://http.cat/404.jpg"]; + } + console.warn(`Nie udało się pobrać listy zdjęć dla ogłoszenia o id: ${noticeId}`, err); return ["https://http.cat/404.jpg"]; } } export const uploadImage = async (noticeId, imageUri) => { - console.log(imageUri); - const formData = new FormData(); const filename = imageUri.split('/').pop(); diff --git a/ArtisanConnect/app.json b/ArtisanConnect/app.json index fab36c1..bbe332e 100644 --- a/ArtisanConnect/app.json +++ b/ArtisanConnect/app.json @@ -2,7 +2,7 @@ "expo": { "name": "ArtisanConnect", "slug": "ArtisanConnect", - "scheme": "ArtisanConnect", + "scheme": "com.hamx.artisanconnect", "version": "1.0.0", "orientation": "portrait", "icon": "./assets/icon.png", @@ -15,7 +15,7 @@ }, "ios": { "supportsTablet": true, - "bundleIdentifier": "com.hamx.ArtisanConnect" + "bundleIdentifier": "com.hamx.artisanconnect" }, "android": { "adaptiveIcon": { @@ -26,7 +26,7 @@ "android.permission.RECORD_AUDIO", "android.permission.CAMERA" ], - "package": "com.hamx.ArtisanConnect" + "package": "com.hamx.artisanconnect" }, "web": { "favicon": "./assets/favicon.png" diff --git a/ArtisanConnect/app/(tabs)/login.jsx b/ArtisanConnect/app/(tabs)/login.jsx index c83452d..fa802de 100644 --- a/ArtisanConnect/app/(tabs)/login.jsx +++ b/ArtisanConnect/app/(tabs)/login.jsx @@ -1,5 +1,5 @@ import React, {useEffect, useState} from 'react'; -import {StyleSheet, ActivityIndicator, SafeAreaView, View} from 'react-native'; +import {StyleSheet, ActivityIndicator, SafeAreaView, View, Platform} from 'react-native'; import {useAuthStore} from '@/store/authStore'; import {useRouter, Link} from 'expo-router'; @@ -18,6 +18,9 @@ import {Ionicons} from "@expo/vector-icons"; import * as WebBrowser from 'expo-web-browser'; import * as Google from "expo-auth-session/providers/google"; import AsyncStorage from "@react-native-async-storage/async-storage"; +import {makeRedirectUri} from "expo-auth-session"; + +import Constants from 'expo-constants'; WebBrowser.maybeCompleteAuthSession(); @@ -27,13 +30,20 @@ WebBrowser.maybeCompleteAuthSession(); export default function Login() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); - const {signIn, isLoading} = useAuthStore(); + const {signIn, isLoading, signInWithGoogle} = useAuthStore(); const router = useRouter(); const [request, response, promptAsync] = Google.useAuthRequest({ androidClientId: "936418008320-d8dfjph5e4r28fcm1rbdfbh5phmbg03d.apps.googleusercontent.com", iosClientId: "936418008320-ohefdfcebd41f6oa2o8phh1mgj9s49sl.apps.googleusercontent.com", - webClientId: "936418008320-btdngtlfnjac1p67guje72m9el5q59a7.apps.googleusercontent.com" + webClientId: "936418008320-btdngtlfnjac1p67guje72m9el5q59a7.apps.googleusercontent.com", + redirectUri: + Platform.OS === 'android' + ? makeRedirectUri({ + scheme: Constants.expoConfig.android.package, + path: '/', + }) + : undefined, }) const handleInternalLogin = async () => { @@ -56,13 +66,18 @@ export default function Login() { }, [response]); const handleGoogleLogin = async () => { - const user = await AsyncStorage.getItem("@user"); + // const user = await AsyncStorage.getItem("@user"); + let user = null; if (!user) { - if(response?.type === "success") { - await getUserInfo(response.authentication.accessToken) + if(response.type === "success") { + user = await getUserInfo(response.authentication.accessToken) + await signInWithGoogle(response.authentication.accessToken); + alert(`Zalogowano jako ${user.email}`); } + } else { - // Set the user in the auth store + console.info("Pobrano użytkownika z AsyncStorage:", JSON.parse(user)); + alert(`Zalogowano jako ${user.email}`); } }; @@ -79,8 +94,8 @@ export default function Login() { } ); const user = await response.json(); - console.log("Pobrano informacje o użytkowniku:", user); await AsyncStorage.setItem("@user", JSON.stringify(user)); + return user; } catch (error) { console.error("Błąd podczas pobierania informacji o użytkowniku:", error); throw error; diff --git a/ArtisanConnect/package.json b/ArtisanConnect/package.json index 72d3615..f54d0dc 100644 --- a/ArtisanConnect/package.json +++ b/ArtisanConnect/package.json @@ -36,7 +36,7 @@ "expo": "^53.0.0", "expo-auth-session": "~6.1.5", "expo-camera": "~16.1.6", - "expo-constants": "~17.1.5", + "expo-constants": "~17.1.6", "expo-image-picker": "~16.1.4", "expo-linking": "~7.1.4", "expo-router": "~5.0.5", diff --git a/ArtisanConnect/store/authStore.jsx b/ArtisanConnect/store/authStore.jsx index f205790..67425ab 100644 --- a/ArtisanConnect/store/authStore.jsx +++ b/ArtisanConnect/store/authStore.jsx @@ -3,12 +3,12 @@ import {createJSONStorage, persist} from "zustand/middleware"; import AsyncStorage from "@react-native-async-storage/async-storage"; import axios from "axios"; -const API_URL = "http://10.0.2.2:8080/api/v1"; +const API_URL = "https://hopp.zikor.pl/api/v1"; export const useAuthStore = create( persist( (set) => ({ - user: null, + user_id: null, token: null, isLoading: false, error: null, @@ -21,9 +21,11 @@ export const useAuthStore = create( password }); - const user = response.data.user; + const user_id = response.data.user_id; const token = response.data.token; - set({user, token, isLoading: false}); + set({user_id: user_id, token: token, isLoading: false}); + + axios.defaults.headers.common["Authorization"] = `Bearer ${token}`; } catch (error) { set({error: error.response?.data?.message || error.message, isLoading: false}); throw error; @@ -33,19 +35,15 @@ export const useAuthStore = create( signUp: async (userData) => { set({isLoading: true, error: null}); try { - console.log(userData); - const response = await axios.post(`${API_URL}/auth/register`, userData, { headers: {'Content-Type': 'application/json'} }); - console.log(response.data); - - const user = response.data.user; + const user_id = response.data.user_id; const token = response.data.token; - set({user, token, isLoading: false}); + set({user_id: user_id, token: token, isLoading: false}); - return user; + axios.defaults.headers.common["Authorization"] = `Bearer ${token}`; } catch (error) { set({error: error.response?.data?.message || error.message, isLoading: false}); throw error; @@ -55,14 +53,14 @@ export const useAuthStore = create( signInWithGoogle: async (googleToken) => { set({isLoading: true, error: null}); try { - const response = await axios.post(`${API_URL}/auth/google`, {token: googleToken}); - - const {user, token} = response.data; - set({user, token, isLoading: false}); + const response = await axios.post(`${API_URL}/auth/google`, {googleToken: googleToken}, { + headers: {'Content-Type': 'application/json'} + }); + const user_id = response.data.user_id; + const token = response.data.token; + set({user_id: user_id, token: token, isLoading: false}); axios.defaults.headers.common["Authorization"] = `Bearer ${token}`; - - return user; } catch (error) { set({error: error.response?.data?.message || error.message, isLoading: false}); throw error; @@ -71,13 +69,12 @@ export const useAuthStore = create( signOut: async () => { try { - // Можно отправить запрос на бэкенд для инвалидации токена await axios.post(`${API_URL}/auth/logout`); } catch (error) { console.error("Logout error:", error); } finally { delete axios.defaults.headers.common["Authorization"]; - set({user: null, token: null}); + set({user_id: null, token: null}); } }, @@ -91,11 +88,11 @@ export const useAuthStore = create( const response = await axios.get(`${API_URL}/auth/me`); - set({user: response.data, isLoading: false}); + set({user_id: response.data, isLoading: false}); return response.data; } catch (error) { delete axios.defaults.headers.common["Authorization"]; - set({user: null, token: null, isLoading: false}); + set({user_id: null, token: null, isLoading: false}); return null; } },