{"id":22957945,"url":"https://github.com/liviuxyz-ctrl/teachingreactnative","last_synced_at":"2025-04-17T12:41:47.658Z","repository":{"id":268028177,"uuid":"903049023","full_name":"liviuxyz-ctrl/TeachingReactNative","owner":"liviuxyz-ctrl","description":"Teaching kids the basics of React Native using Expo","archived":false,"fork":false,"pushed_at":"2025-03-22T14:52:22.000Z","size":424,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-22T15:19:14.807Z","etag":null,"topics":["expo","react-native"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/liviuxyz-ctrl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-12-13T20:25:47.000Z","updated_at":"2025-03-22T14:52:25.000Z","dependencies_parsed_at":"2024-12-13T22:28:07.518Z","dependency_job_id":"01aa9d6a-2c65-4400-b0c1-643de82ed92b","html_url":"https://github.com/liviuxyz-ctrl/TeachingReactNative","commit_stats":null,"previous_names":["liviuxyz-ctrl/teachingreactnative"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liviuxyz-ctrl%2FTeachingReactNative","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liviuxyz-ctrl%2FTeachingReactNative/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liviuxyz-ctrl%2FTeachingReactNative/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liviuxyz-ctrl%2FTeachingReactNative/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/liviuxyz-ctrl","download_url":"https://codeload.github.com/liviuxyz-ctrl/TeachingReactNative/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249342128,"owners_count":21254227,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["expo","react-native"],"created_at":"2024-12-14T17:33:51.712Z","updated_at":"2025-04-17T12:41:47.652Z","avatar_url":"https://github.com/liviuxyz-ctrl.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# **npx create-expo-app@latest**\n# **npx create-strapi-app@latest backend --quickstart**\n# **fnm env --use-on-cd --shell powershell | Out-String | Invoke-Expression**\n# **fnm use 22**\n\n\nBuilding a Simple Banking App with React Native, Expo, and Strapi CMS - Simplified Authentication\n\n\n\nLet's create a simplified authentication system with just username, password, and password confirmation for registration.\n\n\n\nProject Overview\n\n\n\nWe'll build a simplified authentication system with:\n\nUser registration (username and password only)\n\nUser login\n\nLogout functionality\n\n\n\nPrerequisites\n\n\n\nNode.js installed\n\nBasic JavaScript knowledge\n\nA code editor (like VS Code)\n\n\n\nPart 1: Setting Up the Environment\n\n\n\n1.1 Install Required Tools\n\n\n\n# Install Expo CLI globally\nnpm install -g expo-cli\n\n# Install Strapi CLI globally\nnpm install -g strapi\n\n\n\n1.2 Create Project Structure\n\n\n\n# Create and navigate to project folder\nmkdir simple-banking-auth \u0026\u0026 cd simple-banking-auth\n\n# Create frontend and backend folders\nmkdir frontend backend\n\n\n\nPart 2: Setting Up Strapi Backend\n\n\n\n2.1 Initialize Strapi Project\n\n\n\ncd backend\nnpx create-strapi-app@latest . --quickstart\n\n\n\nThis will create a new Strapi project and open the admin panel in your browser.\n\n\n\n2.2 Configure User Permissions\n\n\n\nAfter Strapi starts, create an admin account through the browser interface. Then:\n\n\n\nGo to Settings \u003e Users \u0026 Permissions Plugin \u003e Roles\n\nSelect the \"Public\" role\n\nIn the Permissions tab, find \"Users-Permissions\"\n\nEnable the following:\n\nAuth: callback, register, login\n\nSave your changes\n\n\n\nPart 3: Setting Up React Native Frontend\n\n\n\n3.1 Initialize Expo Project\n\n\n\ncd ../frontend\nnpx create-expo-app .\n\n\n\n3.2 Install Dependencies\n\n\n\nnpm install @react-navigation/native @react-navigation/stack\nnpm install react-native-screens react-native-safe-area-context\nnpm install axios @react-native-async-storage/async-storage\nnpm install react-native-paper\nnpm install react-native-gesture-handler\n\n\n\nPart 4: Frontend Implementation\n\n\n\n4.1 Set Up API Client for Authentication\n\n\n\nCreate src/api/authAPI.js:\n\n\n\nimport axios from 'axios';\nimport AsyncStorage from '@react-native-async-storage/async-storage';\n\n// Replace with your Strapi server address\nconst API_URL = 'http://192.168.1.X:1337'; // Change this to your IP address\n\n// Create axios instance\nconst authAPI = axios.create({\n  baseURL: API_URL,\n  headers: {\n    'Content-Type': 'application/json',\n  },\n});\n\n// Login function\nexport const login = async (identifier, password) =\u003e {\n  try {\n    const response = await authAPI.post('/api/auth/local', {\n      identifier,\n      password,\n    });\n    \n    // Store token and user data in AsyncStorage\n    await AsyncStorage.setItem('jwt', response.data.jwt);\n    await AsyncStorage.setItem('user', JSON.stringify(response.data.user));\n    \n    return response.data;\n  } catch (error) {\n    throw error;\n  }\n};\n\n// Register function - simplified to just username and password\nexport const register = async (username, password) =\u003e {\n  try {\n    const response = await authAPI.post('/api/auth/local/register', {\n      username,\n      email: `${username}@example.com`, // Strapi requires an email, create a dummy one\n      password,\n    });\n    \n    return response.data;\n  } catch (error) {\n    throw error;\n  }\n};\n\nexport default authAPI;\n\n\n\n4.2 Create Authentication Context\n\n\n\nCreate src/context/AuthContext.js:\n\n\n\nimport React, { createContext, useState, useEffect } from 'react';\nimport AsyncStorage from '@react-native-async-storage/async-storage';\nimport { login, register } from '../api/authAPI';\n\n// Create context\nexport const AuthContext = createContext();\n\nexport const AuthProvider = ({ children }) =\u003e {\n  const [isLoading, setIsLoading] = useState(true);\n  const [userToken, setUserToken] = useState(null);\n  const [userInfo, setUserInfo] = useState(null);\n\n  // Login function\n  const loginUser = async (username, password) =\u003e {\n    try {\n      setIsLoading(true);\n      const response = await login(username, password);\n      setUserToken(response.jwt);\n      setUserInfo(response.user);\n      setIsLoading(false);\n      return response;\n    } catch (error) {\n      setIsLoading(false);\n      throw error;\n    }\n  };\n\n  // Register function - simplified\n  const registerUser = async (username, password) =\u003e {\n    try {\n      setIsLoading(true);\n      const response = await register(username, password);\n      setIsLoading(false);\n      return response;\n    } catch (error) {\n      setIsLoading(false);\n      throw error;\n    }\n  };\n\n  // Logout function\n  const logout = async () =\u003e {\n    setIsLoading(true);\n    setUserToken(null);\n    setUserInfo(null);\n    await AsyncStorage.removeItem('jwt');\n    await AsyncStorage.removeItem('user');\n    setIsLoading(false);\n  };\n\n  // Check if user is logged in\n  const isLoggedIn = async () =\u003e {\n    try {\n      setIsLoading(true);\n      const token = await AsyncStorage.getItem('jwt');\n      const user = await AsyncStorage.getItem('user');\n      \n      if (token) {\n        setUserToken(token);\n        setUserInfo(JSON.parse(user));\n      }\n      \n      setIsLoading(false);\n    } catch (error) {\n      console.log(`isLoggedIn error: ${error}`);\n      setIsLoading(false);\n    }\n  };\n\n  // Check for stored authentication on app startup\n  useEffect(() =\u003e {\n    isLoggedIn();\n  }, []);\n\n  return (\n    \u003cAuthContext.Provider\n      value={{\n        isLoading,\n        userToken,\n        userInfo,\n        login: loginUser,\n        register: registerUser,\n        logout,\n      }}\u003e\n      {children}\n    \u003c/AuthContext.Provider\u003e\n  );\n};\n\n\n\n4.3 Create Navigation\n\n\n\nCreate src/navigation/AppNavigator.js:\n\n\n\nimport React, { useContext } from 'react';\nimport { NavigationContainer } from '@react-navigation/native';\nimport { createStackNavigator } from '@react-navigation/stack';\nimport { ActivityIndicator, View } from 'react-native';\n\nimport { AuthContext } from '../context/AuthContext';\n\n// Import screens\nimport LoginScreen from '../screens/LoginScreen';\nimport RegisterScreen from '../screens/RegisterScreen';\nimport HomeScreen from '../screens/HomeScreen';\n\nconst Stack = createStackNavigator();\n\nconst AppNavigator = () =\u003e {\n  const { userToken, isLoading } = useContext(AuthContext);\n  \n  if (isLoading) {\n    return (\n      \u003cView style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}\u003e\n        \u003cActivityIndicator size=\"large\" color=\"#2196F3\" /\u003e\n      \u003c/View\u003e\n    );\n  }\n  \n  return (\n    \u003cNavigationContainer\u003e\n      \u003cStack.Navigator\u003e\n        {userToken === null ? (\n          // Auth screens\n          \u003c\u003e\n            \u003cStack.Screen \n              name=\"Login\" \n              component={LoginScreen} \n              options={{ headerShown: false }} \n            /\u003e\n            \u003cStack.Screen \n              name=\"Register\" \n              component={RegisterScreen} \n              options={{ title: 'Create Account' }}\n            /\u003e\n          \u003c/\u003e\n        ) : (\n          // Home screen\n          \u003cStack.Screen \n            name=\"Home\" \n            component={HomeScreen} \n            options={{ title: 'Banking App' }}\n          /\u003e\n        )}\n      \u003c/Stack.Navigator\u003e\n    \u003c/NavigationContainer\u003e\n  );\n};\n\nexport default AppNavigator;\n\n\n\n4.4 Create Authentication Screens\n\n\n\nCreate src/screens/LoginScreen.js:\n\n\n\nimport React, { useState, useContext } from 'react';\nimport { View, StyleSheet, Alert } from 'react-native';\nimport { TextInput, Button, Text } from 'react-native-paper';\nimport { AuthContext } from '../context/AuthContext';\n\nconst LoginScreen = ({ navigation }) =\u003e {\n  const [username, setUsername] = useState('');\n  const [password, setPassword] = useState('');\n  const { login, isLoading } = useContext(AuthContext);\n\n  const handleLogin = async () =\u003e {\n    if (username === '' || password === '') {\n      Alert.alert('Error', 'Please fill in all fields');\n      return;\n    }\n    \n    try {\n      await login(username, password);\n    } catch (error) {\n      Alert.alert(\n        'Login Error', \n        error.response?.data?.error?.message || 'An error occurred during login'\n      );\n    }\n  };\n\n  return (\n    \u003cView style={styles.container}\u003e\n      \u003cView style={styles.headerContainer}\u003e\n        \u003cText style={styles.title}\u003eBanking App\u003c/Text\u003e\n        \u003cText style={styles.subtitle}\u003eLogin to your account\u003c/Text\u003e\n      \u003c/View\u003e\n      \n      \u003cView style={styles.formContainer}\u003e\n        \u003cTextInput\n          label=\"Username\"\n          value={username}\n          onChangeText={setUsername}\n          mode=\"outlined\"\n          style={styles.input}\n        /\u003e\n        \n        \u003cTextInput\n          label=\"Password\"\n          value={password}\n          onChangeText={setPassword}\n          mode=\"outlined\"\n          style={styles.input}\n          secureTextEntry\n        /\u003e\n        \n        \u003cButton\n          mode=\"contained\"\n          onPress={handleLogin}\n          style={styles.button}\n          loading={isLoading}\n          disabled={isLoading}\n        \u003e\n          Login\n        \u003c/Button\u003e\n        \n        \u003cButton\n          mode=\"text\"\n          onPress={() =\u003e navigation.navigate('Register')}\n          style={styles.linkButton}\n        \u003e\n          Don't have an account? Register\n        \u003c/Button\u003e\n      \u003c/View\u003e\n    \u003c/View\u003e\n  );\n};\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n    backgroundColor: '#f5f5f5',\n  },\n  headerContainer: {\n    alignItems: 'center',\n    marginTop: 100,\n    marginBottom: 50,\n  },\n  title: {\n    fontSize: 28,\n    fontWeight: 'bold',\n    color: '#2196F3',\n  },\n  subtitle: {\n    fontSize: 16,\n    color: '#666',\n    marginTop: 8,\n  },\n  formContainer: {\n    paddingHorizontal: 20,\n  },\n  input: {\n    marginBottom: 15,\n  },\n  button: {\n    marginTop: 10,\n    paddingVertical: 5,\n  },\n  linkButton: {\n    marginTop: 20,\n  },\n});\n\nexport default LoginScreen;\n\n\n\nCreate src/screens/RegisterScreen.js:\n\n\n\nimport React, { useState, useContext } from 'react';\nimport { View, StyleSheet, Alert } from 'react-native';\nimport { TextInput, Button } from 'react-native-paper';\nimport { AuthContext } from '../context/AuthContext';\n\nconst RegisterScreen = ({ navigation }) =\u003e {\n  const [username, setUsername] = useState('');\n  const [password, setPassword] = useState('');\n  const [confirmPassword, setConfirmPassword] = useState('');\n  \n  const { register, login, isLoading } = useContext(AuthContext);\n\n  const handleRegister = async () =\u003e {\n    // Simple validation\n    if (username === '' || password === '' || confirmPassword === '') {\n      Alert.alert('Error', 'Please fill in all fields');\n      return;\n    }\n    \n    if (password !== confirmPassword) {\n      Alert.alert('Error', 'Passwords do not match');\n      return;\n    }\n    \n    if (password.length \u003c 6) {\n      Alert.alert('Error', 'Password must be at least 6 characters');\n      return;\n    }\n    \n    try {\n      // Register the user\n      await register(username, password);\n      Alert.alert(\n        'Success', \n        'Account created successfully!',\n        [\n          { \n            text: 'Login Now', \n            onPress: async () =\u003e {\n              try {\n                // Auto login after registration\n                await login(username, password);\n              } catch (error) {\n                navigation.navigate('Login');\n              }\n            } \n          }\n        ]\n      );\n    } catch (error) {\n      Alert.alert(\n        'Registration Error', \n        error.response?.data?.error?.message || 'An error occurred during registration'\n      );\n    }\n  };\n\n  return (\n    \u003cView style={styles.container}\u003e\n      \u003cView style={styles.formContainer}\u003e\n        \u003cTextInput\n          label=\"Username\"\n          value={username}\n          onChangeText={setUsername}\n          mode=\"outlined\"\n          style={styles.input}\n        /\u003e\n        \n        \u003cTextInput\n          label=\"Password\"\n          value={password}\n          onChangeText={setPassword}\n          mode=\"outlined\"\n          style={styles.input}\n          secureTextEntry\n        /\u003e\n        \n        \u003cTextInput\n          label=\"Confirm Password\"\n          value={confirmPassword}\n          onChangeText={setConfirmPassword}\n          mode=\"outlined\"\n          style={styles.input}\n          secureTextEntry\n        /\u003e\n        \n        \u003cButton\n          mode=\"contained\"\n          onPress={handleRegister}\n          style={styles.button}\n          loading={isLoading}\n          disabled={isLoading}\n        \u003e\n          Register\n        \u003c/Button\u003e\n        \n        \u003cButton\n          mode=\"text\"\n          onPress={() =\u003e navigation.navigate('Login')}\n          style={styles.linkButton}\n        \u003e\n          Already have an account? Login\n        \u003c/Button\u003e\n      \u003c/View\u003e\n    \u003c/View\u003e\n  );\n};\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n    backgroundColor: '#f5f5f5',\n  },\n  formContainer: {\n    padding: 20,\n  },\n  input: {\n    marginBottom: 15,\n  },\n  button: {\n    marginTop: 10,\n    paddingVertical: 5,\n  },\n  linkButton: {\n    marginTop: 20,\n  },\n});\n\nexport default RegisterScreen;\n\n\n\nCreate src/screens/HomeScreen.js:\n\n\n\nimport React, { useContext } from 'react';\nimport { View, StyleSheet } from 'react-native';\nimport { Text, Button, Card } from 'react-native-paper';\nimport { AuthContext } from '../context/AuthContext';\n\nconst HomeScreen = () =\u003e {\n  const { userInfo, logout } = useContext(AuthContext);\n\n  return (\n    \u003cView style={styles.container}\u003e\n      \u003cCard style={styles.card}\u003e\n        \u003cCard.Content\u003e\n          \u003cText style={styles.welcomeText}\u003eWelcome, {userInfo?.username}!\u003c/Text\u003e\n          \u003cText style={styles.infoText}\u003e\n            You have successfully logged in to the Banking App.\n          \u003c/Text\u003e\n        \u003c/Card.Content\u003e\n      \u003c/Card\u003e\n      \n      \u003cButton \n        mode=\"contained\" \n        onPress={logout}\n        style={styles.logoutButton}\n      \u003e\n        Logout\n      \u003c/Button\u003e\n    \u003c/View\u003e\n  );\n};\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n    padding: 16,\n    backgroundColor: '#f5f5f5',\n  },\n  card: {\n    marginBottom: 20,\n  },\n  welcomeText: {\n    fontSize: 20,\n    fontWeight: 'bold',\n    marginBottom: 10,\n  },\n  infoText: {\n    fontSize: 16,\n    color: '#666',\n  },\n  logoutButton: {\n    marginTop: 20,\n  },\n});\n\nexport default HomeScreen;\n\n\n\n4.5 Update App.js\n\n\n\nUpdate the main App.js file:\n\n\n\nimport React from 'react';\nimport { StatusBar } from 'expo-status-bar';\nimport { Provider as PaperProvider } from 'react-native-paper';\nimport { AuthProvider } from './src/context/AuthContext';\nimport AppNavigator from './src/navigation/AppNavigator';\n\nexport default function App() {\n  return (\n    \u003cAuthProvider\u003e\n      \u003cPaperProvider\u003e\n        \u003cStatusBar style=\"auto\" /\u003e\n        \u003cAppNavigator /\u003e\n      \u003c/PaperProvider\u003e\n    \u003c/AuthProvider\u003e\n  );\n}\n\n\n\nPart 5: Running the Application\n\n\n\n5.1 Start the Strapi Backend\n\n\n\ncd backend\nnpm run develop\n\n\n\n5.2 Start the React Native App\n\n\n\ncd frontend\nnpx expo start\n\n\n\nImportant Notes\n\n\n\nIP Address: Make sure to replace http://192.168.1.X:1337 in authAPI.js with your actual local IP address when testing on physical devices.\n\n\n\nEmail Generation: Strapi requires an email for user registration. Our simplified approach automatically creates an email based on the username. In a real app, you might want to collect real email addresses.\n\n\n\nPassword Security: This example uses basic password validation. In a production app, you should implement stronger password requirements.\n\n\n\nConclusion\n\n\n\nYou've now implemented a simplified authentication system with just username and password for your banking app. This includes:\n\n\n\nUser registration with username and password\n\nUsername and password login\n\nAuthentication state persistence\n\nLogout functionality\n\n\n\nThis provides a clean, beginner-friendly foundation for building your banking app in future lessons.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliviuxyz-ctrl%2Fteachingreactnative","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fliviuxyz-ctrl%2Fteachingreactnative","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliviuxyz-ctrl%2Fteachingreactnative/lists"}