diff --git a/App.tsx b/App.tsx index e8880eb..4c3e780 100644 --- a/App.tsx +++ b/App.tsx @@ -5,7 +5,10 @@ import { StyleSheet, Text, TouchableOpacity, - Alert, NativeModules + Alert, + NativeModules, + Image, + ScrollView, } from 'react-native'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { useState, useEffect } from 'react'; @@ -13,6 +16,7 @@ import Login from './src/components/Login'; import Register from './src/components/Register'; import { authAPI } from './src/services/authAPI'; import { networkService } from './src/services/networkService'; + type Screen = 'login' | 'register' | 'home'; const { MyNativeModule } = NativeModules; @@ -22,6 +26,8 @@ function App() { const [isInitialized, setIsInitialized] = useState(false); const [isOnline, setIsOnline] = useState(true); const [currentUser, setCurrentUser] = useState(null); + const [qrCode, setQrCode] = useState(null); + const [isGeneratingQR, setIsGeneratingQR] = useState(false); // Initialize app useEffect(() => { @@ -64,6 +70,7 @@ function App() { const navigateToLogin = () => { setCurrentUser(null); + setQrCode(null); // Clear QR code when navigating to login setCurrentScreen('login'); }; @@ -72,12 +79,14 @@ function App() { const navigateToHome = async () => { const user = await authAPI.getCurrentUser(); setCurrentUser(user); + setQrCode(null); // Clear old QR code when new user logs in setCurrentScreen('home'); }; const handleLogout = async () => { try { await authAPI.logout(); + setQrCode(null); // Clear QR code on logout navigateToLogin(); Alert.alert('Success', 'Logged out successfully'); } catch (error) { @@ -102,6 +111,26 @@ Logged In: ${status.authentication.isLoggedIn ? 'Yes' : 'No'} } }; + const generateQRCode = async () => { + if (!currentUser?.email) { + Alert.alert('Error', 'No user email available'); + return; + } + + setIsGeneratingQR(true); + try { + const qrData = `User: ${currentUser.fullName}\nEmail: ${currentUser.email}`; + const base64Image = await MyNativeModule.generateQRCode(qrData, 300, 300); + setQrCode(base64Image); + Alert.alert('Success', 'QR Code generated successfully!'); + } catch (error) { + console.error('QR Code generation error:', error); + Alert.alert('Error', 'Failed to generate QR code'); + } finally { + setIsGeneratingQR(false); + } + }; + const renderScreen = () => { switch (currentScreen) { case 'login': @@ -120,7 +149,7 @@ Logged In: ${status.authentication.isLoggedIn ? 'Yes' : 'No'} ); case 'home': return ( - + {isOnline ? '🟢 Online' : '🔴 Offline'} @@ -135,7 +164,27 @@ Logged In: ${status.authentication.isLoggedIn ? 'Yes' : 'No'} {currentUser?.email} + {qrCode && ( + + Your QR Code: + + + )} + + + + {isGeneratingQR ? 'Generating...' : 'Generate QR Code'} + + + App Status @@ -150,7 +199,7 @@ Logged In: ${status.authentication.isLoggedIn ? 'Yes' : 'No'} ? 'Your data is synced with the cloud' : 'Working offline - data saved locally'} - + ); default: return null; @@ -191,10 +240,14 @@ const styles = StyleSheet.create({ }, homeContainer: { flex: 1, + backgroundColor: '#f0f0f0', + }, + scrollContent: { justifyContent: 'center', alignItems: 'center', - backgroundColor: '#f0f0f0', - padding: 20, + paddingHorizontal: 20, + paddingBottom: 50, + minHeight: '100%', }, statusBar: { position: 'absolute', @@ -209,6 +262,7 @@ const styles = StyleSheet.create({ shadowOpacity: 0.1, shadowRadius: 4, elevation: 3, + zIndex: 10, }, statusText: { fontSize: 12, @@ -220,28 +274,62 @@ const styles = StyleSheet.create({ color: '#333', marginBottom: 8, textAlign: 'center', + marginTop: 40, }, emailText: { fontSize: 16, color: '#666', - marginBottom: 40, + marginBottom: 30, + }, + qrContainer: { + alignItems: 'center', + marginBottom: 30, + backgroundColor: 'white', + padding: 20, + borderRadius: 12, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.1, + shadowRadius: 4, + elevation: 3, + }, + qrLabel: { + fontSize: 14, + fontWeight: '600', + color: '#333', + marginBottom: 12, + }, + qrImage: { + width: 300, + height: 300, + borderRadius: 8, }, buttonContainer: { - flexDirection: 'row', - gap: 15, + flexDirection: 'column', + gap: 12, marginBottom: 30, + width: '100%', + }, + qrButton: { + backgroundColor: '#10b981', + paddingHorizontal: 20, + paddingVertical: 14, + borderRadius: 25, + alignItems: 'center', }, statusButton: { backgroundColor: '#3bb6d8', paddingHorizontal: 20, - paddingVertical: 12, + paddingVertical: 14, borderRadius: 25, + alignItems: 'center', }, logoutButton: { backgroundColor: '#ff6b6b', paddingHorizontal: 20, - paddingVertical: 12, + paddingVertical: 14, borderRadius: 25, + alignItems: 'center', }, buttonText: { color: 'white', diff --git a/android/app/build.gradle b/android/app/build.gradle index 6552179..4ab006f 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -72,6 +72,11 @@ def enableProguardInReleaseBuilds = false */ def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+' +repositories { + google() + mavenCentral() +} + android { ndkVersion rootProject.ext.ndkVersion buildToolsVersion rootProject.ext.buildToolsVersion @@ -116,4 +121,7 @@ dependencies { } else { implementation jscFlavor } + + // QR Code generation + implementation 'com.google.zxing:core:3.5.1' } diff --git a/android/app/src/main/java/com/lynkeduppro/MyNativeModule.kt b/android/app/src/main/java/com/lynkeduppro/MyNativeModule.kt index 7e062d7..83b5a31 100644 --- a/android/app/src/main/java/com/lynkeduppro/MyNativeModule.kt +++ b/android/app/src/main/java/com/lynkeduppro/MyNativeModule.kt @@ -4,6 +4,12 @@ import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContextBaseJavaModule import com.facebook.react.bridge.ReactMethod import com.facebook.react.bridge.Promise +import android.graphics.Bitmap +import android.graphics.Color +import android.util.Base64 +import java.io.ByteArrayOutputStream +import java.nio.charset.StandardCharsets +import java.security.MessageDigest class MyNativeModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { @@ -16,4 +22,36 @@ class MyNativeModule(private val reactContext: ReactApplicationContext) fun greet(name: String, promise: Promise) { promise.resolve("Hello $name from Android(Kotlin)!") } + + @ReactMethod + fun generateQRCode(data: String, width: Int, height: Int, promise: Promise) { + try { + val bitmap = createQRCodeBitmap(data, width, height) + val base64Image = bitmapToBase64(bitmap) + promise.resolve(base64Image) + } catch (e: Exception) { + promise.reject("QR_CODE_ERROR", "Failed to generate QR code: ${e.message}") + } + } + + private fun createQRCodeBitmap(data: String, width: Int, height: Int): Bitmap { + // Use ZXing library for QR code generation + val qrCodeWriter = com.google.zxing.qrcode.QRCodeWriter() + val bitMatrix = qrCodeWriter.encode(data, com.google.zxing.BarcodeFormat.QR_CODE, width, height) + + val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565) + for (y in 0 until height) { + for (x in 0 until width) { + bitmap.setPixel(x, y, if (bitMatrix[x, y]) Color.BLACK else Color.WHITE) + } + } + return bitmap + } + + private fun bitmapToBase64(bitmap: Bitmap): String { + val outputStream = ByteArrayOutputStream() + bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream) + val byteArray = outputStream.toByteArray() + return "data:image/png;base64," + Base64.encodeToString(byteArray, Base64.DEFAULT) + } }