Notes on learning stage of developing my own React Native education app using Expo Framework

Last updated on 26 May 2025
I am now seriously considering developing my own React Native education app using Expo framework. As a first step towards that, I have started viewing this tutorial (mentioned in my previous post): 
I have yet to study the above GitHub repo code.

As I am watching the tutorial, I felt that I need to start writing some React Native with Expo framework code. So I have started to step-by-step bring in CrudApp functionality of Dave Gray's react-native-course into a regular Expo Framework tab layout boiler plate (with its own dark/light theme code). Also CrudApp is jsx mainly whereas I am using tsx that the boiler plate code uses, and so I am having to convert jsx code to tsx (TypeScript). The project folder name on my PC is: test-DG-ToDos-wBP. Given below are notes related to this work.
===================================
===================================

18 Apr 25:

import a as b ... (using aliases in import statement).

import {
  name as squareName,
  draw as drawSquare,
  reportArea as reportSquareArea,
  reportPerimeter as reportSquarePerimeter,
} from "./modules/square.js";

import {
  name as circleName,
  draw as drawCircle,
  reportArea as reportCircleArea,
  reportPerimeter as reportCirclePerimeter,
} from "./modules/circle.js";

--- end extracts ---

import * as x (creating a module object with all exports of module as members).
Example from MDN: 

import * as Square from "./modules/square.js";
import * as Circle from "./modules/circle.js";
...
const square1 = Square.draw(myCanvas.ctx, 50, 50, 100, "blue");
Square.reportArea(square1.length, reportList);
--- end extracts ---

React Native Expo boilerplate code has:
import * as SplashScreen from 'expo-splash-screen';
----

It also has:
import 'react-native-reanimated';
----
I could not get info. on above usage of import.

=================
TypeScript React Native Flatlist: How to give renderItem the correct type of it's item?,  https://stackoverflow.com/questions/52701665/typescript-react-native-flatlist-how-to-give-renderitem-the-correct-type-of-it :

[Solution ...]
keyExtractor={(item, index) => index.toString()}

=====================

Got the error:
(NOBRIDGE) ERROR  VirtualizedLists should never be nested inside plain ScrollViews with the same orientation because it can break windowing and other functionality - use another VirtualizedList-backed container instead. [Component Stack] 
----
ERROR - VirtualizedLists should never be nested inside plain ScrollViews with the same orientation, https://stackoverflow.com/questions/67623952/error-virtualizedlists-should-never-be-nested-inside-plain-scrollviews-with-th discusses what seems to be the same problem.

I worked around the issue by simply removing the wrapper ParallaxScrollView component. Don't know if this is the right approach. But right now, I don't want to get caught up in this issue.
================

I am using Expo Go. As of now, the app shows the list of ToDos picked up from constants file. The app also shows a Home tab and an About tab (replaced Explore with About). But the status bar is overwritten by the top lines of app screen.

===================================
===================================

18 to 20 Apr

Using SafeAreaView fixed the status bar not being shown issue.
Simple test code without using Themed stuff (Android emulator phone itself is in dark mode) that worked:
import { SafeAreaView } from 'react-native-safe-area-context';
import { Text } from 'react-native';
..
return (
<SafeAreaView>
  <Text style={{color:'white'}}>Hi</Text>
</SafeAreaView>
);
---

Created private GH repo.
...

RN line break in Text:
          Dummy to force scrolling{"\n"}
----

At times, after wake up from hibernation, even if Android emulator is closed and then reopened via Expo on VSCode terminal, it stays frozen and does not get rebooted on clicking on power button or ... but one can close the emulator. Solution is to open Android Studio and 'cold boot' the emulator (which can be done only when it is not running).

--------------
Fixed typescript error with:
    router.push(`/todos/${id}` as Href)

===================

Outstanding known issues:
todos/[id] appears in tab bar at bottom
In edit page, saved changes are not reflected when u get back to main page. But they are reflected on app restart.
In edit page, if one changes data, presses cancel and comes back to edit page, the modified but cancelled data is shown.

======================
Hiding the todos/[id] tab:
      <Tabs.Screen
        name="todos/[id]"
        options={{
          href: null,
        }}
      />
-----------

======================
While the Android app was scrolling the todos, the web app was not scrolling it! Tried commenting out a ThemedView as follows:
        {/* <ThemedView style={styles.stepContainer}> */}
          <Animated.FlatList
            data={todos}
            renderItem={renderItem}
            keyExtractor={(todo:Todo) => todo.id.toString()}
            contentContainerStyle={{ flexGrow: 1 }}
            itemLayoutAnimation={LinearTransition}
            keyboardDismissMode="on-drag"
          />
        {/* </ThemedView> */}
-----
With that, scrolling works both on Android app and on web app. Have decided not to delve into the styling details to understand this behaviour, as of now. I think, if and when needed I can dig into such styling issues and figure them out.

The web app does not seem to pick up system dark mode setting.
20250419-GS-AI-RN-web-app-dark-mode.txt (on my PC) has Google Search AI response to: react native web app does not pick up dark mode setting Windows. Key point is: 
• React Native Web apps run in a web browser, not native Windows apps. Therefore, they don't inherit the system's theme directly. 
• You need to detect the user's preferred color scheme and apply it to your app's styles. [1, 2, 4, 6]  
---------

It gives a solution which has some similarity with DG approach.

The hooks folder in this app (test-DG-ToDos-wBP) has two files:
useColorScheme.ts
useColorScheme.web.ts
----

I don't understand exactly how this file naming style works. Does the web app version pick up the web.ts file if available?
Have to check if in the only boiler plate code, web app picks up Windows dark mode.
Added a console.log statement to the web.ts file:
import { useColorScheme as useRNColorScheme } from 'react-native';
...
  const colorScheme = useRNColorScheme();
  console.log (`useColorScheme (web): colorScheme returned by useRNColorScheme is ${colorScheme}`)

Browser console shows the following which is repeated many times:
useColorScheme (web): colorScheme returned by useRNColorScheme is light
---------

So useColorScheme of react-native does NOT give Windows dark mode setting (which is set to dark on my PC now).

Based on Google Search AI for this issue, am trying out:
  const colorSchemeAppearance = Appearance.getColorScheme()
  console.log (`useColorScheme (web): colorSchemeAppearance returned by Appearance.getColorScheme() is ${colorSchemeAppearance}`)

--------
But that also does not work. Console output:
useColorScheme (web): colorSchemeAppearance returned by Appearance.getColorScheme() is light
-------
Dropping this issue as of now.
A temporary workaround is to turn on 'High Contrast' on Windows (Left Alt + Left Shift + Prt Scr). That works OK for this app.
==========================

Debugging issue: "In edit page, saved changes are not reflected when u get back to main page. But they are reflected on app restart." :

Added console.log statements to check useEffect() invocation for fetchData().
In DG tutorial program, which does not use tabs, useEffect() is invoked for fetchData() when user comes back to / page from todo edit page.
But in this app (test-DG-ToDos-wBP) useEffect() is NOT invoked when user comes back to / page from todo edit page.

Why?
How to trigger a useEffect, when returning to a previously opened tab #436, https://github.com/expo/router/discussions/436 seems to raise the same issue. The questioner himself provides a workaround but asks if there is a better way.

An answer points to: useFocusEffect, https://reactnavigation.org/docs/use-focus-effect/

Note that Expo router seems to be based in some way on React Navigation and IFIRC I read that for some details about Expo router behaviour one should read React Navigation.

Another related post but somewhat different use case: Trigger UseEffect whenever i switch tabs in React Native Tab Navigator, https://stackoverflow.com/questions/74544651/trigger-useeffect-whenever-i-switch-tabs-in-react-native-tab-navigator .

https://docs.expo.dev/router/basics/common-navigation-patterns/ indicates that perhaps I should use a Stack navigator in the Home tab for Home page and edit Todo page. But I am not sure if that is tied to this useEffect() issue.
[I just confirmed that switching tabs from Home to About and back to Home does not trigger the fetchData() related useEffect. Perhaps this is where useFocusEffect would be relevant.]

Another way to implement the Edit Todo page would be as a modal.

=============
useFocusEffect() works as expected but it recommends using useCallback which introduces an issue. The useCallback has to be given a dependency array. If todos is not provided as dependency, it seems to always return the initial value (does not invoke fetchData). If todos is provided as a dependency, when a todo is added it gets into some long, possibly infinite, loop kind of issue (probably because todos are changed within the useCallback parameter function.

So I am dropping useFocusEffect() approach (at least as of now).

=================

Trying out using stack navigator for Todos and individual todo edit page. It broke / route but /todos route works. Need to fix this issue.
However, using stack navigator does not seem to resolve issue of useEffect in todos/index not getting invoked when coming back from individual todo edit page. 
==========
Am exploring using a global context to fix this issue.
Tried to follow DG ThemeContext approach but faced lot of Typescript issues.
TypeScript on useContext, https://stackoverflow.com/questions/74173658/typescript-on-usecontext Option 1 helped me to resolve most of them.
But I was stuck for some time on "Cannot find namespace 'AppContext'" error.
Solution was to change the .ts file to .tsx [DG project had .js file even though it had a jsx element in it. That led me to make the file .ts.] Ref: Cannot find namespace 'ctx' error when creating Context with react - typescript, https://stackoverflow.com/questions/57242264/cannot-find-namespace-ctx-error-when-creating-context-with-react-typescript : "Your file extension is most likely .ts instead of .tsx."

....
Got Typescript error for:
  const {todosChanged, setTodosChanged} = value
---
Solution was to use:
  const todosChanged = value!.todosChanged
  const setTodosChanged = value!.setTodosChanged
---
Checking for null would put the const defined variables within the if block.
Seems like TS does not have a specific solution for this. Ref: How to use exclamation mark with object destructuring, https://stackoverflow.com/questions/77615791/how-to-use-exclamation-mark-with-object-destructuring
-----

The above approach fixed index page update on edit page changing todo.
The edit page continues to show old entry value when cancelled and then re-entered for same entry. The code issue is that the fetch is done based on a useEffect with dependency on id, and so when id does not change, the fetch is not done again.
Fixed the issue with a cancelled state variable which is set to true just before router.navigate on Cancel, and this cancelled state variable is added to dependencies of useEffect that does the fetch.
----

Fixed stack issue by:
* Renaming todos folder to (todos)
* _layout.tsx in (tabs) having:
      <Tabs.Screen
        name="(todos)"
        options={{
          title: 'Home',
          tabBarIcon: ({ color }) => <IconSymbol size={28} name="house.fill" color={color} />,
        }}
      />
      <Tabs.Screen
        name="about"
        options={{
          title: 'About',
          tabBarIcon: ({ color }) => <IconSymbol size={28} name="info" color={color} />,
        }}
      />
---
* index.tsx having:
    router.navigate(`/${id}` as Href)
---
* [id].tsx having:
            router.navigate('/' as Href)
...
                        router.navigate('/' as Href)
---

Ref: MrPiao answer on Oct 11, 2024 at 1:51 in: How to Set a Specific Tab as the Initial Route in Expo Router Without an index.tsx?,
===========

Minor issue: http://localhost:8081/todos brings up Edit Todo page! 
I think I got the cause. If the todo is not found in fetchData, the user should be shown not found page or redirected to Home. Problem is that / followed by anything (including not-found) goes to Edit page! Made changes to send user to Home page if id not found.

While testing on phone, I see the message, "Cannot read property 'length' of null even though To Do List is displayed. I checked that web version does not show such a console error message. Got the issue (a console.log statement). Fixed.

But Edit page is not opening (on press) on phone!
Used React native debugger for first time. It is awesome and easy to use as it is similar to React web development debugging using Chrome.
With breakpoints, was able to see that it invokes Edit page but as I do not have a loaded variable for the fetch and associated check, it seems that before the await finishes the code hits the idFound false check and gets redirected to Home page.
Don't know how it works on web though but clearly it is a bug in my code.
Also DG code does not cater to this loading aspect of fetch in the edit page. But it does not have a redirect and perhaps that's why it works.
Added loading stuff  in edit page and that fixed above issue.
But new issue is that after a few visits to edit page, it gets stuck on the edit page and clicking Save or Cancel does not result in it going back to home page. Instead even clicking on home tab  shows that edit page OR sometimes shows a blank page!
Looking at edit page code, I realized that I am not resetting loading page back to false. But when do I do that? Ideally, I should do that on the page component being created with a new id passed to it. But does the Edit page component get recreated on new id? will have to debug using RN debugger.

Hmm. I think the main issue I need to understand is that when tabs or stack navigator is used, when does the component get recreated. More to the point, if one is using tabs, can one have a list page and an edit page invoked from it, like one can have in typical React apps? Or should one use modals? I think I will understand this better by getting more exposure to current RN apps code rather than me continuing to try stuff on my own for this app. So maybe I should just try to stablize this app a little and then keep it aside, and continue with the Simon Grimm tutorial.

On the other hand, I think I should do one more debugging session and see if I can get more data about the problem. Then I can decide whether to try to spend some time to fix it or skip it. The debugging experience may be useful for future.
...
Did some debugging.
Was able to replicate the issue in Android emulator and do debugging session with Android emulator. 'j' on 'npm start' VSCode terminal starts the debugger.
router.navigate('/') does not result in edit page execution stopping!
==============

Navigation lifecycle, https://reactnavigation.org/docs/2.x/navigation-lifecycle gives info. on component mount/unmount which may apply to Expo router too. My understanding is that it says that screens are not unmounted when user uses tabbar to switch to another screen. It also provides a way to find out if a user is leaving or coming back to a screen. "React Navigation emits events to screen components that subscribe to them." The related events are: willFocus, willBlur, didFocus and didBlur.

===========
Now I am getting the error in VSCode/npm start:
Failed to open the React Native DevTools.
Error: Runtime.evaluate
====

I tried it multiple times but it is not working (with Android emulator). Earlier on today it had worked.

Another person seems to face similar problem: See DJFriar on Mar 18 entry: [0.76] React Native DevTools — Issues and Feedback, https://github.com/react-native-community/discussions-and-proposals/discussions/819

=============
When I made the header visible in (todos) stack _layout page, I observed that router.navigate was pushing stuff on the stack in Edit page and not popping the stack. I wonder whether that is the cause of the weird issue.
Using router.push in index page and router.back in edit page seems to resolve the issue.
=========
So now most of DG tutorial functionality seems to be working in this boilerplate related project using TypeScript. There is some styling issue especially on web but I don't want to spend much time on styling issue now. I need to do an additional round of quick testing on web and on Android emulator as well as phone. If I don't find any significant issues, I will go back to the Simon Grimm tutorial.

===================================
===================================

21 & 22 Apr. 2025

On 21st April, pressing j on VSCode running 'npm start' (Expo), successfully brought up React Native Debugger (I am running the app on Android emulator). I had restarted the system since my last failed atttempts. Perhaps restart did the trick.

Using the RN debugger, I noted that router.back() in the Edit page seems to immediately switch to index page and code related to rendering of edit page is NOT executed (whereas router.navigate was not doing so and code in edit page including the code related to rendering of the edit page was being executed).

A minor issue is the Edit page allows for empty Todo. I think this may be an issue in DG tutorial code too.

App is working OK in Android emulator, phone and web.
---

In hooks\useColorScheme.web.ts, made these minor changes:
  if (hasHydrated) {
    return 'dark';
    // return colorScheme;
  }

  // return 'light';
  return 'dark';
-----

Now the web app shows in dark mode. Also checked that Android app (on phone) switches to light mode when system setting changes to light mode. So above .web.ts code only impacts web app.

=====================================================
Switching back to Simon Grimm (SG) EduApp

20250421-2126-lms-react-native.zip (on my PC) has the zipped file of VSCode clone of https://github.com/Galaxies-dev/lms-react-native
20250421-2129-lms-api.zip (on my PC) has the zipped file of VSCode clone of https://github.com/Galaxies-dev/lms-api

Now I plan to go back to earlier commits in these two projects and study the code from initial commits onwards.
Using Git — how to go back to a previous commit, https://medium.com/swlh/using-git-how-to-go-back-to-a-previous-commit-8579ccc8180f :
git checkout <commit-id> .
[Last period is required.]
----------

Init to win it.: 4f7ba9042b0dd3f8f8377fc00dc1b85667bec856

git checkout 4f7ba9042b0dd3f8f8377fc00dc1b85667bec856 .
git checkout 4f7ba90 .

git add .
git commit -m "Reverting to <4f7ba90>"

To break link with remote, used: git remote remove origin

But this does not show me the limited files for commit: 4f7ba90 as seen in GitHub.
It struck me now that I should have done this on a new branch.

Is it possible to go back to a specific commit to see what your code looked like at the point?, https://www.reddit.com/r/github/comments/ypemjy/is_it_possible_to_go_back_to_a_specific_commit_to/

The above seems to be saying something similar.
I took a closer look at the files. It seems that all the files in GitHub 'Browse files' for this commit are shown in local project too and when I checked some of the key files, the contents seem to match. But local folder has additional files. Perhaps this procedure does not delete such additional files.

Solved it. I had to additionally use (as suggested by David_AnkiDroid in above reddit.com article):
git reset --hard 4f7ba9042b0dd3f8f8377fc00dc1b85667bec856
----

Now the additional files are not shown.
...

Now am running npm install. It took quite some time but got done successfully.
npm start gave the error:
Error: NativeWind only supports Tailwind CSS v3
---

My project's package.json has this entry:
    "tailwindcss": "^4.0.5"
----
I saw package.json of the final commit of the project on GitHub. It has this entry:
    "tailwindcss": "3.x"
----

Hmm. So did SG face the same error and then go to an earlier version of tailwindcss? Yes, GitHub file history shows that in the commit: WIP auth and layout., the package.json entry has been changed to:
    "tailwindcss": "3.x"
----

On modifying my package.json to use 3.x and then running npm start, I am able to get the metro scan code. But on trying to start web version, I get some Metro error and the bundling fails. Android bundling also fails. Error messages:
Web Bundling failed 4200ms node_modules\expo-router\_error.js (133 modules)
Unable to resolve "react-native-css-interop/jsx-runtime" from "node_modules\@expo\metro-runtime\src\error-overlay\ErrorOverlay.tsx"
Web Bundling failed 7729ms node_modules\expo-router\entry.js (51 modules)
Android Bundling failed 13076ms node_modules\expo-router\entry.js (534 modules)
Unable to resolve "react-native-css-interop/jsx-runtime" from "node_modules\expo-router\build\qualified-entry.js"
----
I recall that he said we have to use development build. So maybe I should try that. But ...

I think this approach has not worked out. Renamed the current project dir as unstable...
========
Unzipped the GitHub clone zip for the RN project. Opened that folder in VSCode.
Ran npm install. It gave some high vulnerabilities. Ran npm audit fix and now there are 0 vulnerabilities.
As per video https://youtu.be/fO3D8lNs10c?t=871 , I am next running:
npx expo prebuild
That succeeded.
Next:
npx expo run:android
(SG does it on Mac and uses run:ios)
-----
It opened the emulator and started building the app.
Got this error:
> Task :expo-modules-core:compileDebugKotlin FAILED
e: This version (1.5.14) of the Compose Compiler requires Kotlin version 1.9.24 but you appear to be using Kotlin version 1.9.25 which is not known to be compatible.  Please consult the Compose-Kotlin compatibility map located at https://developer.android.com/jetpack/androidx/releases/compose-kotlin to choose a compatible version pair (or `suppressKotlinVersionCompatibilityCheck` but don't say I didn't warn you!).

> Task :react-native-reanimated:compileDebugJavaWithJavac
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: Some input files use unchecked or unsafe operations.                                                                                                      
Note: Recompile with -Xlint:unchecked for details.

FAILURE: Build failed with an exception.

==========
Below links seem to be related but are too complicated:
=====================

I think I should try creating a fresh (new) project and add the code step-by-step. Perhaps fresh project creation will pick up compatible components to my current installation (like Kotlin version).

Going ahead with:
npx create-expo lms
It succeeded.
Now I will try developer build on this starter app.
npx expo prebuild
That succeeded. It got done quite quickly.
Next:
npx expo run:android
Msg: "Starting a Gradle Daemon, 1 stopped Daemon could not be reused, use --status for details". Hmm. Looks like I have to logout and relogin to stop that daemon which also may be the process preventing me from renaming the project folder.
It finished initialization and configuration and is now into execution.
It succeeded taking around 11 minutes and 42 seconds till metro started. The build itself related message: "BUILD SUCCESSFUL in 11m 5s"
The app opened up in Android emulator and seems to be working correctly.
I could also start RN debugger, set a breakpoint in index.tsx, and on reload of app with 'r' on metro, the app on emulator paused with code execution reaching the breakpoint in RN debugger.

That's great! So a basic Expo starter app development build works OK on my PC.
From SG lms project folder's .gitignore, added following to my project .gitignore:
ios/
android/
.env
.env.local
----

After logout and relogin, renamed the last mentioned above SG project folder to build-failed-lms-react-native. This project will still be useful to me to open in VSCode and see SG 's code and perhaps copy-paste code from it to my starter project mentioned above. Renamed my starter project folder to "ravi-lms". Associated it with private GH repo: https://github.com/ravisiyer/SG-EduApp-Ravi-LMS .

Great! I now have a base with which to step-by-step try out the SG tutorial project code as I go through his video.
Ran: npm run android. That failed.
Ran: npx expo run:android. That too failed. Related error message (part):
FAILURE: Build failed with an exception.

* What went wrong:
Could not determine the dependencies of task ':app:compileDebugJavaWithJavac'.
> Could not resolve all dependencies for configuration ':app:debugCompileClasspath'.
   > Could not resolve project :react-native-gesture-handler.
     Required by:
         project :app
      > No matching variant of project :react-native-gesture-handler was found. The consumer was configured to find a library for use during compile-time, preferably optimized for Android, as well as attribute 'com.android.build.api.attributes.AgpVersionAttr' with value '8.6.0', attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'debug', attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm' but:
          - No variants exist.
   > Could not resolve project :react-native-reanimated.
     Required by:
         project :app
      > No matching variant of project :react-native-reanimated was found. The consumer was configured to find a library for use during compile-time, preferably optimized for Android, as well as attribute 'com.android.build.api.attributes.AgpVersionAttr' with value '8.6.0', attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'debug', attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm' but:
          - No variants exist.
----

Signed out and signed in again and changed project folder name back to 'lms' (from 'ravi-lms').
Ran: npx expo run:android
Now the build was successful and got done quickly (build message: "BUILD SUCCESSFUL in 39s") and the app got loaded in emulator and ran correctly.
Hmm. That's some learning. In React Native with development build, can't rename project folder after build and reuse old build. IFIRC, React projects can be renamed without any such problem. Don't know if React Native using Expo Go only projects (without development build) can be renamed without facing such issues.
...
After PC restart, npm run android ... that too worked with "BUILD SUCCESSFUL in 44s".

Changed 'Welcome' in index.tsx to 'Hi there' and saved. Virtually immediately, the Android app showed 'Hi there' instead of 'Welcome'. So Fast Refresh feature works well in this development build, as of now at least.

npm install nativewind tailwindcss@^3.4.17 react-native-reanimated@3.16.2 react-native-safe-area-context
Got done successfully.
In package.json:
    "tailwindcss": "^3.4.17"
----
So suitable version of tailwindcss seems to have got installed.
Next: npx expo customize metro.config.js .. done
npx expo customize babel.config.js .. done
https://www.nativewind.dev/getting-started/installation Step 2:
npx tailwindcss init .. done
Replaced xxx with (from above link):
/** @type {import('tailwindcss').Config} */
module.exports = {
  // NOTE: Update this to include the paths to all of your component files.
  content: ["./app/**/*.{js,jsx,ts,tsx}"],
  presets: [require("nativewind/preset")],
  theme: {
    extend: {},
  },
  plugins: [],
}
----
Added to content: above:
, './components/**/*.{js,jsx,ts,tsx}'
----
Created global.css as per above nativewind.dev link step 2
Replaced babel.config.js contents with above nativewind.dev link step 3
Replaced metro.config.js contents with above nativewind.dev link step 4

Next, as per video, ran: npm run reset-project ... (chose n to delete boiler plate stuff)
Followed above nativewind.dev link step 5 by adding "import "./global.css" in _layout.tsx file
Changed it to: import "@/global.css"
above nativewind.dev link step 6 is not needed as app.json already has required lines.
above nativewind.dev link step 7 (TypeScript) leads to: https://www.nativewind.dev/getting-started/typescript
Followed above and created nativewind-env.d.ts in project root dir with contents:
/// <reference types="nativewind/types" />
----
Added :root colors from SG project global.css to my project global.css
Similarly added theme.extend from SG tailwind.config.js

Skipping npx expo prebuild as I have done that earlier. But don't know if 'npm run reset-project' that I ran, needs me to redo the prebuild. Let me see.

npx expo install expo-dev-client ... I had not done this earlier. Seems to be optional to run expo prebuild. But I will do it now to be in sync with SG tutorial

npx expo install expo-dev-client ... done

Now I think I should run npx expo prebuild again as the expo-dev-client may require a rebuild. 
As per https://docs.expo.dev/workflow/continuous-native-generation/, 'npx expo prebuild --clean' seems to be what I need to run:
The --clean option deletes any existing native directories before generating. Re-running npx expo prebuild without the --clean option will layer changes on top of the existing files, which is faster, but may not produce the same results in some cases.
---------

Ran npx expo prebuild --clean ... It suggested a git commit before running it ... Did that.
It finished prebuild very quickly. Its messages:
√ Cleared android code
√ Created native directory
√ Updated package.json | no changes
√ Finished prebuild
---------

Next: 
npx expo run:android
That got done successfully in around 6 to 7 mins including app opening in Android emulator ... Build message, "BUILD SUCCESSFUL in 5m 40s"
'm' on metro opens up dev menu in Android app! That's great. This dev menu seems to be part of Expo dev client, if I got SG correctly.
...
Was able to make trivial changes to index.tsx but using TailwindCSS for styling. It worked rightaway.
Saved as commit: Trivial changes in screen but using TailwindCSS

Installed Tailwind CSS IntelliSense VSCode extension. Now colors and intellisense are coming up for Tailwind styling.
Used text-primary class (CSS variable in global.css). That needed reload of app ('r' on metro). Now it shows related text in blue (text-primary color).
So whatever SG is doing in the video (till https://youtu.be/fO3D8lNs10c?t=1221 (20 mins odd)), similar stuff is working in my project. So the base is set well, I think.

For my own info., am trying web version ('w' on metro): it took some time for web bundling but eventually the web app displayed with expected text and styling. Made a commit in project: "Used CSS variable text-primary from global.css". This is a good starter point for other RN projects with Tailwind. 

Created 20250422-1746-RN-TailwindStarter-XF.zip (on my PC) having only source code of above "lms" project folder (around 1 MB only).
Currently the lms folder size is 5.02 GB! Huge amount of space is taken. node_modules is 3.6 GB and android is 1.4 GB.
===================================
===================================

22 & 23 Apr. 2025

Build modern websites with the most customizable Headless CMS, https://strapi.io/

npx create-strapi-app@latest lms-api --typescript [ https://youtu.be/fO3D8lNs10c?t=1278 ]

Got an error:
  Error   Oh, it seems that you encountered an error while installing dependencies in your project

          Don't give up, your project was created correctly

          Fix the issues mentioned in the installation errors and try to run the following command:

          cd C:\Users\{Username}\NotInBackupScript\SG-EduApp\lms-api && npm install
----
Switched to Jio SIM (Airtel SIM data may have got over though no message yet about 100%). 

Now running npm install in lms-api ... That seems to have succeeded.

npm run strapi lists the commands available.

Ran: npm run develop (as per video)

I got some errors:|
[2025-04-22 22:24:36.757] info: Strapi started successfully
[2025-04-22 22:24:37.395] http: GET /admin (40 ms) 200
X [ERROR] Could not resolve "@strapi/admin/strapi-admin"
================

http://localhost:1337/admin was opened and shown as blank page.

I think I should re-run npm install after cleaning up earlier npm install.
Deleting node_modules folder seems to be recommended way of doing a clean npm install. npm-ci seems to be a more involved thing, https://docs.npmjs.com/cli/v11/commands/npm-ci

Deleted node_modules
Then ran npm install (again).
Got done quickly.
npm run develop
Got the same or similar error.
Running: npm audit fix:
added 13 packages, removed 17 packages, and audited 1319 packages in 1m
...
17 moderate severity vulnerabilities
----
npm run develop
Now it worked. No error and register admin screen is shown in browser: http://localhost:1337/admin/auth/register-admin
Created admin emailid/pwd [riyer02@gmail.com/...]

=========================
May need python for strapi using SQLite - https://www.python.org/downloads/
python is installed in my PC. Checked with python command in terminal:
Python 3.13.1 (tags/v3.13.1:0671451, Dec  3 2024, 19:06:28) [MSC v.1942 64 bit (AMD64)] on win32
---


=====================
=====================

23 Apr.

Copy-pasted contents of config\plugins.ts from SG-lms-api to my lms-api (adds clerkId).

Adding text field clerkId to User in Content-Type Builder - Short text (type).
Save

Copied .gitignore of SG-lms-api to my lms-api
---------------

I think I should try to build SG-lms-api as then I will be able to access its strapi admin panel to copy data into my lms-api. OR I can simply study SG-lms-api.
In SG-lms-api:
Ran npm install
Got high and critical vulnerabilities message
npm audit fix did not fix it. So am running:
npm audit fix --force
Got: "17 moderate severity vulnerabilities" but no high and critical vulnerabilities.
npm run develop gave errors.
 Loading Strapi[ERROR]  There seems to be an unexpected error, try again with --debug for more information 

   Error: Middleware "strapi::session": App keys are required. Please set app.keys in config/server.js (ex: keys: ['myKeyA', 'myKeyB'])                  │ 
   at instantiateMiddleware                                                                                                                        
=====================
Hmm. So this is not working out.

Deleting node_modules and doing npm install again.
This time I got: "17 moderate severity vulnerabilities" [without doing npm audit fix or npm audit fix --force]
But npm run develop gave the same/similar error.

============
Error: Middleware “strapi::session”: App keys are required, https://forum.strapi.io/t/error-middleware-strapi-session-app-keys-are-required/24948 :

Create an .env file in the base directory of the project.
Add these values to it:
APP_KEYS={random-string-doesn’t-matter}
API_TOKEN_SALT={random-string-doesn’t-matter}

After this, everything works flawlessly.
-----

Hmm. Saw that there is a .env.example file. Copied it and renamed copy to .env
I did not modify anything in it (though it suggests modification with 'tobemodified') as a test.
npm run develop now ran properly. The admin page opened up and I am able to see Collection Types of Course, Lesson etc.
But I was not able to see data for these types.
---------

Ran: npx strapi import -f backup.tar.gz  [and said y to prompt(s)]  Ref: https://youtu.be/fO3D8lNs10c?t=1832
That got done successfully.
Ran: npm run develop
Now I can see course and lesson data including (what seems to be) rich text, images and a video (in media library).
=============

To pick up strapi provider from SG-lms code  Ref: https://youtu.be/fO3D8lNs10c?t=2063

===============================
===============================

25 Apr. 2025

Copied providers\StrapiProvider.tsx from SG-lms to my lms
npm i @tanstack/react-query
npm i @dev-plugins/react-query
Copied DUMMY.env from SG-lms to my lms and commented out all lines except: 
EXPO_PUBLIC_STRAPI_API_URL=http://localhost:1337
----

Copied types/interfaces.ts from SG-lms to my lms
Commented out BlocksContent related 2 lines

==============

"Integrate complete user management in minutes. Free for your first 10,000 monthly active users and 100 monthly active orgs. No credit card required."
Signed up 
Created lms-video app with Google and Apple as sign-in options (to be in sync with SG).
Following Expo guide in next step for lms-video app; also following SG video
Ran:
npm install @clerk/clerk-expo
npm install @clerk/clerk-react
npm install expo-secure-store
npm install @clerk/types

Copied EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=xxxxxxxx entry from clerk.com Expo to .env file
Copied <ClerkProvider> ... to root layout file. But now there is a lot of difference between SG video and Clerk Expo doc. Will need to try out stuff.

Following Clerk docs now:
Am using app/_layout.tsx file of step 4 Configure the Token Cache with Expo, with additional line of:
import "@/global.css"
----

In my case, Clerk dashboard -> Configure -> Native Applications had "Enable Native API" already selected (on). SG video says he had to set it on.

Did the following:
npx expo run:android
The build got over without taking too much time. "BUILD SUCCESSFUL in 2m 28s". Then the Android app was loaded into emulator and it showed the index page as expected.
Saved current files as commit: Added strapi provider and clerk auth code but minimal usage of them; App shows index page properly

Now I will try out a hybrid of Clerk documentation and SG-lms app minimal code to step-by-step add the functionality that the SG-video adds.

Was able to step-by-step proceed on login screen. Made few commits along the way. Yet to finish ....
Tmp Restart TS: https://youtu.be/fO3D8lNs10c?t=2880

======================

About React Native: The ULTIMATE Way to Build Apps in 2025! (You’ll NEVER Go Back), https://www.youtube.com/watch?v=acj7-3-EQWM , 7 min. 19 secs., Simon Grimm, Apr. 2025

================

15 May 2025

Getting back to Simon Grimm's React Native lms tutorial after gap of around 3 weeks.

Top-level folder: C:\Users\{Username}\NotInBackupScript\SG-EduApp
Frontend folder: lms ; Remote repo:  https://github.com/ravisiyer/SG-EduApp-Ravi-LMS.git
Backend folder: lms-api ; No remote repo as of now
SG project repo (IFIRC): SG-lms-api ; Remote repo:  https://github.com/Galaxies-dev/lms-api.git

Opened lms folder in VSCode.
Ran 'npm run android'
It opened emulator and then said:
Starting a Gradle Daemon (subsequent builds will be faster)
...
BUILD SUCCESSFUL in 1m 38s
...
 Using development build
...
› Installing C:\Users\{Username}\NotInBackupScript\SG-EduApp\lms\android\app\build\outputs\apk\debug\app-debug.apk
› Opening exp+lms://expo-development-client/?url=http%3A%2F%2F192.168.183.151%3A8081 on Medium_Phone_API_35
----

Android emulator showed login screen of app.
Great! 
Next opened lms-api in VSCode.
Ran 'npm run develop'
That started strapi and http://localhost:1337/admin/auth/login showed Strapi login screen.
Was able to login using local admin credentials saved elsewhere.
Strapi admin shows the User 'COLLECTION TYPE' with various fields like username, email and also clerkId.

With this, I think I am the point I had stopped lms-api work on 25 Apr. 2025.
----

Visited clerk.com which showed me as logged in. Went to Dashboard and was shown "lms-video" (app?) Overview. Was shown a message:
"Watching for users" ... "You're just a couple steps" .. "away from your first user."
Changed the page instructions to Expo.
Confirmed that steps 1 to 4 of this page has been implemented in lms app.

I think on frontend side (lms), I am at the point mentioned at the end of 25 Apr. 2025 entry, which states:
Was able to step-by-step proceed on login screen. Made few commits along the way. Yet to finish ....
Tmp Restart TS: https://youtu.be/fO3D8lNs10c?t=2880 .
----
I will need to view some minutes earlier to above timestamp in the video and connect that with the code I have seen now for both lms and lms-api. Then I can restart the work of coding the app along with watching the tutorial video.
------

16 May 2025

In lms project:
npx expo install expo-crypto [SG tutorial TS: https://youtu.be/fO3D8lNs10c?t=3204 ]
Added "Tailwind Fold" extension to VSCode. Ctrl + Alt + A toggles Tailwind CSS fold.

I uncommented some of the code in login.tsx. I also had to bring in lot of code from SG _layout.tsx into lms project _layout.tsx. Some of that code had to be commented to avoid errors. Note that folder (on my PC) build-failed-lms-react-native seems to have SG lms code.

I enabled onPress for Google in login.tsx. Then in the web app, I clicked it. It led a dialog from Google asking for confirmation (two such dialogs). On accepting them, while the app continued to be on login page as I have not coded for what happens after login, strapi admin shows a User with my regular gmail id! Clerk dashboard - https://dashboard.clerk.com/ - also shows my regular gmail id as an added user! Fascinating!
ClerkId in strapi admin was not being shown by default. Had to use settings to show CLERKID field. For my gmail login, the CLERKID field is also populated.
Git committed code: Basic login using Google

TS: https://youtu.be/fO3D8lNs10c?t=3648 (Part 5: Protecting our App Layout)
Following along with video.
Created (app)/(authenticated) folders
Created (app)/_layout.tsx
... Also copied required files. May need to comment out some of the code in it.

Video TS: https://youtu.be/fO3D8lNs10c?t=4482

============

I struggled a little to use gmail login on Android Emulator (I had not logged in to Google on the emulator). The issue was that the verification code for 2 step authentication was not being received on my phone. Eventually was able to do it using some g.co/verifyaccount link suggested by Google. Now that added the a/c in Clerk but did not add it in Strapi database! The api process was running and a quick look at the console log (VSCode terminal) did not show any errors. May need to check this out later.

Was able to view http://localhost:8081/profile after login on web. It showed tabs at bottom. Don't know how to do the same on Android (deep link?) ...Looks like it may need some support in the Android app itself.

Git committed code: /profile on web shows tabs and related pages
==========

In app/_layout.tsx, I had commented some code that navigated logged in users to the index page. After I uncommented that and reloaded Android app., it showed the home page with tabs like on web. On web, on logging out and trying to go to /profile resulted in login page being shown. Relogin took me to index page. So basic auth stuff for app seems to be in place.

Git committed code: basic auth works for web and Android
============

Defining a different web layout (with page header instead of tabs).
TS: https://youtu.be/fO3D8lNs10c?t=4864

Copied related files from SG lms to my lms project. Now web app uses different navigation UI (page header instead of tabs).

To check that Android app works as earlier with tabs, I wanted to try it out on my Samsung phone.
The APK installable is app-debug.apk in folder: C:\Users\{Username}\NotInBackupScript\SG-EduApp\lms\android\app\build\outputs\apk\debug.
It currently is around 100 MB.
Copied the apk file to Samsung phone and installed it. Ran it and then scanned QR code shown in VSCode terminal of running lms app. The app got displayed on Samsung phone and worked as expected. Tabs were shown as UI.
============

Picking up data from Strapi
npm install @strapi/blocks-react-renderer
Got errors ... Key error message: 
npm error command C:\WINDOWS\system32\cmd.exe /d /s /c husky install
npm error 'husky' is not recognized as an internal or external command,
npm error operable program or batch file.
----
ChatGPT suggestion:
npm install husky --save-dev
---- 
That worked.
Uncommented related code in components/HomeBlock.tsx
=======================

Seems like lms-api was the wrong project to use. 23-Apr (2025) comments show that while I did create lms-api, I later switched to SG-lms-api which I was able to get to run after some relatively minor steps like npm audit fix --force and then setting up the .env file. Importantly, I did the import stuff in SG-lms-api which has the courses and other data SG has created for the app. I could do the same import in lms-api too. But I think I will try to now switch to SG-lms-api. Note that I did not make any code changes to lms-api project after I restarted this work on 15 May 2025. Only the database data has changed with new user being added.

If I switch to SG-lms-api, I may need to recreate user(s) and also delete old user data from Clerk before I do that, to keep SG-lms-api and Clerk in sync.

Switched to SG-lms-api. Moved lms-api to a folder named: SeeminglyNotUsed
Deleted the two users I had created earlier from Clerk.com/
Logged in using my regular gmail a/c from lms web. That created the associated user in both Clerk and strapi.

GET http://localhost:1337/api/home
Instead of Postman to try above, I used browser: http://localhost:1337/api/home . That gave expected JSON data (but omitting image data).
http://localhost:1337/api/home?populate=* gave JSON data for images too.

TS: https://youtu.be/fO3D8lNs10c?t=5585  [Home page loading with content from Strapi]

types\interfaces.ts - following commented lines had to be uncommented:
// import { BlocksContent } from '@strapi/blocks-react-renderer';
...
export interface HomeInfo {
  title: string;
  // content: BlocksContent;
  image: string;
}

------
The images fail to show on index page.
ChatGPT message I sent:
I am using @strapi/blocks-react-renderer.
I get this error:
http://localhost:8081/uploads/eugenia_08_fashanable_modern_empty_background_in_Kpop_music_tyl_be76d1f3_0847_484c_9bb2_a29ba0f2184d_a66345afdf.png 404 (Not Found)
----

http://localhost:1337/uploads/eugenia_08_fashanable_modern_empty_background_in_Kpop_music_tyl_be76d1f3_0847_484c_9bb2_a29ba0f2184d_a66345afdf.png is served correctly by browser.

=======
ChatGPT analysis:
React Native is trying to fetch the image from Metro because the URL is relative.
Add the Strapi base URL either when you render the image or inside Strapi’s server.js, and the 404 disappears.
---------

Based on one suggestion from ChatGPT, I added the "👈 NEW" line below to strapi server.js:
export default ({ env }) => ({
  host: env('HOST', '0.0.0.0'),
  port: env.int('PORT', 1337),
  url : env('PUBLIC_URL', 'http://192.168.1.42:1337'), // 👈 NEW
  app: {
    keys: env.array('APP_KEYS'),
  },
});
-----------

I restarted api and frontend. I am still facing same error.


================
================

18 May 2025


Based on another suggestion from ChatGPT and above ref. page, I modified config\plugins.ts to:
export default ({ env }) => ({
  'users-permissions': {
    config: {
      register: {
        allowedFields: ['clerkId'],
      },
    },
  },
  upload: {
    config: {
      providerOptions: {
        baseUrl: env('PUBLIC_URL', 'http://192.168.1.42:1337'),
      },
    },
  },
});
----

It didn't work. I think the upload plugin is no longer supported in current Strapi version which I am using: 5.12.6
version 5 docs: https://docs.strapi.io/dev-docs/plugins/upload shows page not found
version 4 docs: https://docs-v4.strapi.io/dev-docs/plugins/upload shows the appropriate page.

On informing ChatGPT about it, it responded:
Thanks for confirming — and yes, you’re absolutely right: as of Strapi v5.12.6, the upload plugin configuration has changed significantly, and the approach that worked in v4 (with plugins.js) no longer applies in the same way.
----

======

I don't know how SG did not face the same issue. https://github.com/Galaxies-dev/lms-api/blob/main/package.json uses strapi version 5. Related lines:
    "@strapi/plugin-cloud": "5.9.0",
    "@strapi/plugin-users-permissions": "5.9.0",
    "@strapi/strapi": "5.9.0",
----

My SG-lms-api package.json's related lines are:
    "@strapi/plugin-cloud": "5.9.0",
    "@strapi/plugin-users-permissions": "^5.12.6",
    "@strapi/strapi": "^5.12.6",
----

Both are on version 5.
=========

ChatGPT gave the front-end suggestion (which it had given earlier). I had to modify it slightly as given below. With that the image gets shown on index page.

File lib/getStrapiMedia.ts :
const STRAPI_URL = process.env.EXPO_PUBLIC_STRAPI_API_URL ?? 'http://localhost:1337';

export const getStrapiMedia = (url?: string) => {
  if (!url) return '';
  return url.startsWith('http') ? url : `${STRAPI_URL}${url}`;
};
---

File: components\HomeBlock.tsx
...
import { getStrapiMedia } from '@/lib/getStrapiMedia';
...
  return (
    <Animated.View className="w-full pb-14" entering={FadeIn.duration(200).easing(Easing.ease)}>
      {/* <Image source={{ uri: homeInfo?.image }} className="w-full h-40" /> */}
      <Image source={{ uri: getStrapiMedia(homeInfo?.image) }} className="w-full h-40" />
      {/* <View className="p-4">{blockContent && <BlocksRenderer content={blockContent} />}</View> */}
      <View className="p-4">
        {blockContent && 
        <BlocksRenderer
          content={blockContent}
          blocks={{
            image: ({ image }) => (
              <Image
                source={{ uri: getStrapiMedia(image.url) }}
                style={{ width: image.width ?? 300, height: image.height ?? 300 }}
                resizeMode="cover"
              />
            ),
          }}
        />}
      </View>
...
--------

But the image size is cropped vertically. Commenting the style and resizeMode lines did not solve the issue.
The text was not being shown but when I changed Chrome dark mode setting to light, the text got shown. 

Now the display is similar to https://youtu.be/fO3D8lNs10c?t=5794 . But my app has some extra stuff as I have used the final SG code.
========

In .env file of strapi, I had to make the following changes to solve an issue of strapi giving a slightly confusing message of wrong server IP address (as my public url had a hardcoded IP which did not match current (dynamically allotted) IP):
HOST='localhost'
...
PUBLIC_URL=http://localhost:1337
---

I also changed server.ts to:
export default ({ env }) => ({
  host: env('HOST', 'localhost'),
  port: env.int('PORT', 1337),
  url : env('PUBLIC_URL', 'http://localhost:1337'), // 👈 NEW
  app: {
    keys: env.array('APP_KEYS'),
  },
});
------

Anyway the backend code changes did not solve the image url issue. So I reverted most of the code changes above, except for .env file changes which I retained.

The frontend home page shows the home page (image and text) using the backend with reverted code.

lms commit: Index page shows image and text on web in light mode

The Android app on my Samsung phone does not show index page (neither image nor text) ... Debugging it.
It does not seem to get homeInfo data.
Plan to comment out tanstack stuff for easier debugging.

It later struck me that the issue could be that localhost on Samsung phone is a different IP than my PC's IP. Perhaps that's why the strapi call to get homeInfo data fails. Need to check whether it works on Android emulator on PC.

==========
==========

19 May 2025

Android emulator seems to run on different IP from Windows host.
I think that's why the emulator is failing to get data from strapi.

current PC Ip is: 192.168.183.151
Using the ip in lms env instead of localhost.
The app opens now in both emulator and Samsung phone but it does not display any data from strapi.
This happens even when I am using my regular gmail id on Samsung phone which id is registered as a user in strapi (as it was created via web)
At times there is an "Error fetching data from Strapi: TypeError: Network request failed" message on the app. [Which seems to be from getCourses in StrapiProvider.tsx.]
There are no messages on strapi server indicating that nothing from the Android app is being received by strapi server.

While debugging, my console.log statements are not appearing!
Console logs not showing up in development build, https://www.reddit.com/r/expo/comments/1epte59/console_logs_not_showing_up_in_development_build/ seems to report on a similar issue.

Trying out console.warn(). The app then stops working and just shows a black screen on continue with Google!
React Native Android debugging using Expo Development build seems to be a big hassle (I think it was easier with Expo Go).

===================
===================

20 May 2025

Yesterday, I got an error about no space on Android emulator for installation of app! I had to delete some old apps which fixed it. Later I need to see how to increase size of emulator virtual device RAM or delete some unused standard apps like youtube if that's possible.
...

Fixed fetching data from strapi issue for Android app on emulator with following changes to :
In backend (strapi api) .env file:
# HOST='localhost'
HOST='192.168.183.151'
PORT=1337
# PUBLIC_URL=http://localhost:1337
PUBLIC_URL=http://192.168.183.151:1337
----

The frontend .env file remained the same for backend api url related stuff:
# EXPO_PUBLIC_STRAPI_API_URL=http://localhost:1337
EXPO_PUBLIC_STRAPI_API_URL=http://192.168.183.151:1337
----

I also confirmed using 'ipconfig /all' that 192.168.183.151 was the IP address currently of my PC.

The issue with earlier version of strapi backend .env file was that http://192.168.183.151:1337 was not getting a response. I had thought that I can keep the .env file 'localhost' entries as is and that ip address request would be matched with localhost if ip address was same as localhost IP. But that does not seem to be the case.

The Android app also gets strapi data on phone now.

The index page data (both image and text) is displayed on the Android app but horizontal scrolling is required. So some styling changes would be needed to fix that.

Looks like I should try in strapi .env:
HOST='0.0.0.0'
and commenting out PUBLIC_URL line.
Note that earlier (after fresh creation of project IFIRC), I did have HOST='0.0.0.0' in the .env file. Somewhere down the line as I was trying to fix
the issue of strapi data not being fetched on Android app, I changed it.
ChatGPT says, "'0.0.0.0' is a special IP address that tells the server to accept connections from any IPv4 address, not just localhost."
---

Above .env changes worked.
-------
Now only one frontend Android app in emulator is running. Now the console.log statements in getHomeInfo in StrapiProvider.tsx printing results.data are also shown on VSCode terminal. These have to come from the Android emulator app code (web frontend has not been opened). Hmm. Don't know why in the earlier attempt at debugging done yesterday the console.log statements in handleSignInWithSSO function in login.tsx for Android app were not showing.

Deleted alternate gmail user in clerk.com. Logged in again in Android app with alternate gmail user. This time around, as strapi access from Android app is working, the alternate gmail user account got created both in clerk and strapi.

The console.log statements in handleSignInWithSSO function in login.tsx still are not showing up on VSCode terminal. Perhaps that's related to some other issue. Other console.log statements are shown.

==========================

I am exploring possibility of expo development build for Android on DML (low config Dell Mini Laptop). I will not use Android emulator but if the setup creates a dev build I will copy that to phone.

Needed to install chocolatey and then jdk on DML.
Now running the build command in android folder of project:
./gradlew installDebug (suggested by ChatGPT)
It is downloading gradle files (zip).
It installed Gradle 8.10.2.
It is now starting a Gradle Daemon.
It gave an error about (Android) SDK location not found.

Checked and found that Android 14.0 ("UpsideDownCake") with API level 34 was installed (and Android 15.0 ("VanillaIceCream") was NOT installed).

https://reactnative.dev/docs/set-up-your-environment says RN needs ... "Building a React Native app with native code, however, requires the Android 15 (VanillaIceCream) SDK in particular."

Following:
Select the "SDK Platforms" tab from within the SDK Manager, then check the box next to "Show Package Details" in the bottom right corner. Look for and expand the Android 15 (VanillaIceCream) entry, then make sure the following items are checked:

Android SDK Platform 35
Intel x86 Atom_64 System Image or Google APIs Intel x86 Atom System Image
Next, select the "SDK Tools" tab and check the box next to "Show Package Details" here as well. Look for and expand the Android SDK Build-Tools entry, then make sure that 35.0.0 is selected.
--------
Also set up env. variables as described in my post: Notes on using React Native without Expo framework and Expo Go, https://raviswdev.blogspot.com/2025/02/notes-on-using-react-native-without.html

Now retrying gradle build.
It seems to have gone beyond last error point and seems to now be downloading NDK (side by side) [which IFIRC is a big package]

Added 5 GB data for Jio for Rs.100 - 90 day validity (My plan gets over today with new plan queued behind; but this 90 day validity is irrespective of my current plan expiring today.
Configuration got done fully.
Now into Execution (0%). 1 hr. 19 mins already. One factor for this long time taken may be that it is downloading and installing stuff like NDK which may be one-time work for the system (not repeated even for other RN projects). I think slow speed of Jio during evening time (now it is 7.41 PM) has been another major factor for the build taking so long. I switched to Airtel (after buying 5 GB data for Rs.100 but with only 30 day validity). I changed mobile data SIM from Jio to Airtel while gradle build was ongoing. It only restarted current package download (react-android-0.76.9-debug.aar 196.6 MB) which is now happening at better speed than with Jio. I think I should have switched to Airtel earlier. I was concerned about gradle build interruption on switching mobile data SIM from Jio to Airtel - it was doing NDK (side by side) download on Jio when I decided to buy Jio data pack. If I had switched then perhaps the whole NDK (side by side) download (700 to 800 odd MB it seems) would have restarted.
Of course, yet another factor for slow speed is that the DML is a low-config PC.

85% executing - 1h 55m

It then failed with ninja error. I had faced that earlier. See More notes on React Native, Expo and Ignite; Some misc. stuff, https://raviswdev.blogspot.com/2025/04/more-notes-on-react-native-expo-etc.html. Trying fix mentioned there:

Using Android Studio, "Click on the "-" checkbox under SDK Tools tab.", Installed CMake latest among 3.31.xx version of 3.31.6
Repeating gradlew build ... It failed ... I closed and reopened VSCode to ensure any new/changed environment variables get picked up.
Repeating gradlew build ... The build failed in 53 seconds.
In VSCode terminal, executed: $Env:CMAKE_VERSION = '3.31.6'
Repeating gradlew build ... It seems to have crossed the last error point ... Now 86% executing in 1m 47s
Got the error:
> Task :app:buildCMakeDebug[arm64-v8a] FAILED
C/C++: ninja: error: Stat(safeareacontext_autolinked_build/CMakeFiles/react_codegen_safeareacontext.dir/C_/Users/ravi-user/Projects/VSCode/ReactNative/SG-EduApp-Ravi-LMS/node_modules/react-native-safe-area-context/common/cpp/react/renderer/components/safeareacontext/RNCSafeAreaViewShadowNode.cpp.o): Filename longer than 260 characters   

BUILD FAILED in 16m 7s
611 actionable tasks: 46 executed, 565 up-to-date
------------

As DML is already slow I am hesitant to change its registry to allow larger length filenames like I did on my regular PC. My regular PC seemed to have become a little slower to start, after that.

=========
From ChatGPT:

What You Actually Want
To use the development build of your app (created via eas build or eas dev build) and then launch your Metro bundler locally, you should not use npx expo run:android.

Instead, do this:

✅ Preferred Workflow (No USB / Emulator)
Ensure you have a dev build installed on your Android device (via QR code, OTA, or manual install).

In your project directory, run:
...
npx expo start --dev-client
This will start the Metro bundler and show a QR code.

On your Android device, open the Expo development client app, and scan the QR code.

✅ Your development build will now connect to your local bundler.

🔄 Quick Summary: Difference in Commands
Command Purpose
npx expo run:android Compiles and installs app on USB-connected device or emulator
npx expo start --dev-client Starts Metro server for existing development build — shows QR code

==========================

Tried 'npx expo start --dev-client'. As ChatGPT said, it did not start Android emulator and showed a QR code. I was able to scan the QR code on phone which started the installed app (dev build). Great!

================

Set up lms folder (from latest zip) on DML 
npm install succeeded.
Next ran 'npx expo start --dev-client' . Metro started quickly. On phone, scanned the QR code. That started the android bundling which took quite some time (maybe 5 to 10 mins) - much more time than on PC. Some Reanimated not initialized error was shown on app on phone as well as on terminal on DML. But it then showed the index page without strapi data and without 'courses' tab.

set up Ravi-lms-api folder on DML which was a copy of Ravi-lms-api on PC (node_modules folder was not compatible with node version on DML and so had to be deleted and then downloaded using npm install). Strapi ran properly (note database had also been copied over). Strapi admin worked on browser as expected.

Was also able to run web app on DML. It picked up data from strapi on DML.

But dev app on phone could not get data from strapi on DML (possibly because it was built with strapi ip on PC in .env file).

Changing frontend JS code resulted in immediate change in app on web on DML. On phone, the app had to be reloaded after which the change was shown. Note that this was without any local (or EAS) rebuild of the app. My understanding of ChatGPT explanation is that JS changes results in change on app as Metro bundler handles the matter but config changes or dependencies changes will need app rebuild (local or EAS) to get reflected.

So there is a possibility of EAS build when using DML as local build there failed (due to filename length issue) and anyway local build was taking very long on DML. Need to explore this possibility.

Initiated eas build from DML for lms.
The build failed. Main issue was assets files were missing. commented assets in .gitignore. Reinitiated eas build.
Build failed again but went further than last time. Error:
Plugin [id: 'expo-module-gradle-plugin'] was not found in any of the following sources:

Based on above, removed ^ from package.json entries for expo-secure-store and @expo/vector-icons ... Reinitiated eas build
Failed again... Removed all ^ from dependencies (but some remain in devdependencies) ... eas build
I think I have exhausted my free builds for now ... Wait time for build is about 190 minutes!
=========

I had a very interesting discussion with ChatGPT on EAS build,  React Native vs React, Metro/webpack, (public link)  https://chatgpt.com/share/682da650-909c-8013-a534-87ddb38181c8 . I also copy pasted the chat contents into a Google Doc.

Some key points from the chat are given below:
  1. "If you're iterating rapidly during development and can afford the initial setup effort, local builds offer more speed and control. However, EAS cloud builds are easier for most use cases and ideal when you don’t want to deal with local native setup."
  2. "... use a Development Build (aka Custom Dev Client) and take advantage of the local Metro bundler — without needing to re-run EAS builds every time."
    1. [Ravi modified] EAS build ['eas build --profile development --platform android'] — to create your custom dev client. Rebuild only when native code changes or new native dependencies are added.
    2. "Install that build on your device or simulator."
    3. "Use npx expo start to run the Metro bundler locally." [Ravi: That worked. Expo CLI seems to have detected that I am using a development build (perhaps by presence of .android directory) and so it started with using a development build. But to ensure it starts with a development build --dev-client should be added to above command.]
    4. "Your dev client will connect to the Metro bundler and fetch updated JS bundles — no new EAS build required for JS/React code changes."
  3. "If you change any EXPO_APP_XXX values in .env:"
    1. "You do not need a new EAS build if:"
      1. "You're using a development build, and"
      2. "You're only using those values in JavaScript (JS) code, and"
      3. "You restart the Metro bundler properly." ... "npx expo start --clear" ... "This clears the bundler cache and re-evaluates app.config.js, which re-imports the .env file and updates process.env."
    2. " You do need a new EAS/local build if:"
      1. "The EXPO_APP_XXX value is used in a way that impacts native config (e.g., bundle ID, permissions, plugins), or"
      2. "You’re using Release Channels, Updates, or Static Configs, where the value gets baked into the native binary."
  4. "Metro is the JavaScript bundler used by React Native. It takes all your JavaScript (and related assets) and packages them into a single optimized bundle that can be run on a mobile device. Think of it like Webpack, but specially designed for React Native’s needs."
  5. "Metro does not natively handle web — but it can be used with web via Expo." ... "Web support comes through Expo + Webpack, not Metro" ... "Expo CLI" - " starts Metro for mobile" and "starts Webpack only if/when you use the web platform (w key or --web flag)".
  6. "Webpack is a module bundler for JavaScript applications — primarily used for web apps. It takes all your project’s files — JavaScript, CSS, images, etc. — and bundles them together into optimized files that can be delivered to and run in a web browser."
  7. "React Native on Android handles JavaScript using a bundled JavaScript engine (like Hermes), and translates that to native Android components via a communication bridge. Android itself doesn’t run JavaScript natively."
  8. ".. React Native apps usually have some performance overhead compared to fully native Android apps (written in Kotlin or Java). But how much that matters depends on the app's complexity and use case."
I plan to put up a separate post on key points of the React Native vs React web part of the chat. Will update this post with its link later on. [23 May 2025 Update: Here's that post: ChatGPT on using one React Native codebase for mobile and web apps: promise vs. realityhttps://raviswdev.blogspot.com/2025/05/chatgpt-on-react-native-vs-react-web.html .]

=================
=================

21 May 2025

The EAS build submitted yesterday night/today early morning  also failed.
ON DML
Now, after installing @react-native-community/cli, ran npx react-native doctor which gave same errors about Adb, Gradlew etc. but which may not be relevant as I am doing EAS build.
Now running, npx expo install --check, as suggested by EAS build log.
It asked for two packages to be upgraded. Did so.
Next running npm audit fix.
It did not report any issues.
Now, issued: eas build --platform android --profile development
Above command is slightly different from previous recent eas builds which did not have --profile development IFIRC.
Build is queued. Wait time is about 30 mins.
Got what seems to be same error.
Added to package.json:
  "overrides": {
    "expo-font": "13.0.4"
  }
 ----
But npm audit fix complained about above ... So removed it.
 
Also removed ^ from devDependencies
Doing what I expect is the last attempt at eas build for this project. If it fails, it is better to stop as I am using local build on PC which works. I was trying to get EAS build to work from DML as a test case for future.
The above build also failed with same/similar error. Stopping efforts to try to fix this.
==========================
==========================

Possibly 21 May 2025 and surely 22 May 2025

For expo-module-gradle-plugin error, ChatGPT suggested, "npm install expo-modules-core".
I also asked why local build worked without that package. ChatGPT responded: 

Local build works because:
Your local machine has a cached or globally available copy of the expo-module-gradle-plugin (from another Expo project, node_modules cache, etc.).

Gradle or npm/yarn might resolve it from a local source that’s not tracked in package.json.

It’s lenient if a native plugin isn’t explicitly listed in package.json but still available locally.

❌ EAS build fails because:
EAS runs builds on a clean cloud environment where only the dependencies declared in your project (especially package.json) are installed.

No global cache.

No assumptions.

If expo-modules-core isn’t declared in package.json, the plugin cannot be resolved, and you get:
...
Plugin [id: 'expo-module-gradle-plugin'] was not found...
===================

So on DML, I did "npm install expo-modules-core" and queued an eas build

That too failed. Expo Doctor on eas gave an additional error:
✖ Check dependencies for packages that should not be installed directly
The package "expo-modules-core" should not be installed directly in your project. It is a dependency of other Expo packages, which will install it automatically as needed.
Advice:
Remove these packages from your package.json.
----

The eas build proper gave this error:
ℹ️  Applying gradle plugin 'expo-dev-launcher-gradle-plugin' (expo-dev-launcher@5.0.35)
 ℹ️  Applying gradle plugin 'expo-module-gradle-plugin' (expo-modules-core@2.3.13)
Property 'kspVersion' is not defined. Using default value: '2.0.21-1.0.28'. Please ensure the 'expo-root-project' plugin is applied to your root project.
FAILURE: Build failed with an exception.
* Where:
Script '/home/expo/workingdir/build/node_modules/expo-modules-autolinking/scripts/android/autolinking_implementation.gradle' line: 333
* What went wrong:
A problem occurred evaluating root project 'lms'.
> Failed to apply plugin 'com.facebook.react.rootproject'.
   > A problem occurred configuring project ':app'.
      > Failed to apply plugin 'com.android.internal.library'.
         > 'com.android.library' and 'com.android.application' plugins cannot be applied in the same project.
===========

So ChatGPT suggested solution which seemed to be simple, did not work.

=============

On PC, fixed the Android home page horizontal clipping problem with following code changes (w-screen instead of w-full; added w-screen in View) in HomeBlock.tsx:
    // <Animated.View className="w-full pb-14" entering={FadeIn.duration(200).easing(Easing.ease)}>
    <Animated.View className="w-screen pb-14" entering={FadeIn.duration(200).easing(Easing.ease)}>
      <Image source={{ uri: getStrapiMedia(homeInfo?.image) }} className="w-screen h-40" />
      {/* <Image source={{ uri: getStrapiMedia(homeInfo?.image) }} className="w-full h-40" /> */}
      <View className="w-screen p-4">
---------

Now both on Android emulator and phone, the index page is showing correctly.

But https://youtu.be/fO3D8lNs10c?t=6299 shows that SG does not face a problem with w-full for mobile app display. Is it an iOS vs Android issue?

Changing Tailwind CSS (NativeWind) classes to JSX elements does not get reflected immediately in Android app. It needs a reload of app to get reflected. ChatGPT agrees: "React Native + Metro Bundler + Expo does not support full CSS-like hot-reloading." .. "So even if you switch from 'w-full' to 'w-screen', NativeWind might still be showing you the previous style until a full reload happens."

Commit: fixed the Android home page horizontal clipping problem

Now I am in sync with what I had seen in the tutorial. Moving forward in SG tutorial.

Dark theme works for web but not for Android app. Using same code as SG but it works for him on iOS - https://youtu.be/fO3D8lNs10c?t=6481 .
ChatGPT does not give a clear solution but says that I should use @strapi/blocks-react-native-renderer instead of @strapi/blocks-react-renderer . But I can't find @strapi/blocks-react-native-renderer !!!!

https://strapi.io/integrations/reactnative

      <View className= "w-screen p-4 text-white"> shows text in white in dark mode on Android app.

But uncommented version of below code works only for web and not for Android app. In dark mode, white text is not shown.
      {/* <View className= {colorScheme === 'dark' ? "w-screen p-4 text-white" : "w-screen p-4"}> */}
    Mabye I am missing something here. Need to relook at this later when I am more fresh.
I think I should try applying style to View instead of className. Style may be more direct for React Native.
====================

After some debugging hours, chats with ChatGPT and browsing on net, I understood the issue and the solution (got it step wise).
Key steps:
1) After quite some time including IFIRC ChatGPT as well as Internet suggestions being tried out, I checked whether the Android app gets the color scheme correctly from useColorScheme (in HomeBlock.tsx). It does not. It always reports 'light'. [Either before or after, I added '"userInterfaceStyle": "automatic"' for android and ios in app.json which did not solve the problem.] 

2) ChatGPT gave a simple top-level index.tsx which I used (and renamed top-level _layout.tsx to a .txt file) to check whether the simple app gets colorScheme correctly. It did! The index.tsx code:
import { View, Text, StyleSheet, useColorScheme, Appearance } from 'react-native';

export default function Index() {
  const colorScheme = useColorScheme();
  const staticScheme = Appearance.getColorScheme();

  console.log("useColorScheme():", colorScheme);
  console.log("Appearance.getColorScheme():", staticScheme);

  return (
    <View style={[styles.container, colorScheme === 'dark' ? styles.dark : styles.light]}>
      <Text style={styles.text}>useColorScheme(): {colorScheme}</Text>
      <Text style={styles.text}>Appearance.getColorScheme(): {staticScheme}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  dark: {
    backgroundColor: '#000',
  },
  light: {
    backgroundColor: '#fff',
  },
  text: {
    fontSize: 20,
    color: '#00f', // Just to help visibility
  },
});
--- end code ---

3) Next ChatGPT suggested simple code being added to (top-level) _layout.tsx of my regular app which essentially console logged the color scheme. Related code snippet given by ChatGPT:
import React, { useEffect } from 'react';
import { useColorScheme } from 'react-native';

function DebugColorScheme() {
  const scheme = useColorScheme();
  useEffect(() => {
    console.log('Color scheme inside layout:', scheme);
  }, [scheme]);

  return null;
}

// Then inside RootLayout's render return:
<>
  <DebugColorScheme />
  {/* rest of your layout */}
</>
-------
Here too, the console.log reported the scheme correctly (when Android app was running).

4) Adding a console.log to (tabs)/index.tsx showed that even index.tsx was getting the scheme correctly but HomeBlocks.tsx was not! 

5) I shared code of (tabs)/index.tsx and HomeBlocks.tsx with ChatGPT and specified above behaviour. ChatGPT caught it very quickly then! At times, ChatGPT is really awesome at bug detection.

The issue is that HomeBlock.tsx component uses 'use dom'. As per ChatGPT:
'use dom';
at the top of HomeBlock.tsx. This enables React Native for Web's DOM mode in Expo Router.

What does that mean?

React Native for Web DOM mode renders your component as a web React DOM component, outside the React Native context.

That means hooks like useColorScheme() no longer hook into React Native's Appearance API but instead use the browser environment.

And importantly, React Native's native Appearance API isn't accessible there, so it defaults to "light" or the browser's scheme, or sometimes an outdated value.

Effect of 'use dom' on hook behavior

The "use dom" directive makes your component run in the web DOM renderer, which can cause useColorScheme() to behave differently or lose native theme awareness on Android.

What about (tabs)/index.tsx?
It does not have 'use dom' so it runs as a React Native component, correctly reflecting Android’s native Appearance API.

So useColorScheme() there works fine.

---end ChatGPT extract ----

The solution (suggested by ChatGPT but it was obvious to me too after the above explanation):
".. in (tabs)/index.tsx you get the colorScheme correctly, so pass it down as a prop to HomeBlock."
Next, in HomeBlock, use the passed prop instead of using useColorScheme.

That fixed the issue in the Android app running on emulator (latest build).
And also fixed it on Android app running on phone (older dev build). 

I informed ChatGPT and then wrote to it:
Well, you have been a great help. Though as I had not told you about 'use dom' (as I did not suspect that to be an issue), I did spend fair amount of time trying out your earlier suggestions like "userInterfaceStyle": "automatic" in app.json for android and ios and app rebuilds and clearing metro cache. But eventually we got the issue. You guided me well in this debugging process. thanks.
--- end my message to ChatGPT ---

I have publicly shared this ChatGPT chat: https://chatgpt.com/share/682f5cd3-e320-8013-88df-e214989032ab

Commited code: Fixed dark mode issue for Android app
===============
===============

Possibly 22 May 2025 to 24 May 2025

Video restart point: https://youtu.be/fO3D8lNs10c?t=6548

Around https://youtu.be/fO3D8lNs10c?t=6541 , SG shows HomeBlock.tsx with HTML tags instead of RN tags. I think that's possible because of 'use dom'.

One issue in the Android app in dark mode is that the top status bar is not shown. I had faced a similar problem in some other Android app, probably RN Android app and recall that I did some Status Bar specific stuff. First asked ChatGPT which suggested the exact code. Then used Copilot in VSCode to get the code needed and also applied it to the file using Copilot. That fixed the issue. Related code in app\_layout.tsx (only StatusBar line was added in RootLayout() function):
import { StatusBar } from 'expo-status-bar';
...
export default function RootLayout() {
  const colorScheme = useColorScheme();

  return (
    <ClerkProvider
      tokenCache={tokenCache}
      publishableKey={publishableKey}
      waitlistUrl="http://localhost:8081/">
      <GestureHandlerRootView style={{ flex: 1 }}>
        <ClerkLoaded>
          <QueryClientProvider client={queryClient}>
            <StrapiProvider>
              <ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
                <StatusBar style={colorScheme === 'dark' ? 'light' : 'dark'} />
                <InitialLayout />
              </ThemeProvider>
            </StrapiProvider>
          </QueryClientProvider>
        </ClerkLoaded>
      </GestureHandlerRootView>
    </ClerkProvider>
  )
}
=================
Ts: https://youtu.be/fO3D8lNs10c?t=6909

Regarding image not showing in index page in Android app, I got the fix through ChatGPT suggestion involving BlocksRenderer. But SG seems to provide the fix @ around https://youtu.be/fO3D8lNs10c?t=5451 . I recall seeing that but had not realized that the GitHub repo code did not have this additional code he adds in the video. I caught it when he did something similar for courses.

I think I will try out SG code and comment out ChatGPT suggested BlocksRenderer code for index page image.
Tried out Copilot which used __DEV__
so asked ChatGTP which said:
Best Practice (React Native & Expo)
✅ Use __DEV__ when checking dev vs production mode in React Native or Expo.

Only use process.env.NODE_ENV if you're trying to write code that should work in both React Native and web (React) in a cross-platform app like with Expo for Web.
==== end ChatGPT message extract ====

Hmm. SG uses process.env.NODE_ENV but let me try __DEV__
That works. The image is shown in Android app as well as in web app. Related code in StrapiProvider.tsx:
  const getHomeInfo = async (): Promise<HomeInfo> => {
    try {
      const response = await fetch(`${baseUrl}/api/home?populate=*`);

      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      const result = await response.json();
      const image = (__DEV__)
       ? `${baseUrl}${result.data.image.url}`  // In Development
       : `${result.data.image.url}`

      result.data = {
        ...result.data,
        image,
      };
      console.log('result.data', result.data);
      return result.data;
    } catch (error) {
      throw error;
    }
  };
=================
HomeBlock.tsx modified code:
      <Image source={{ uri: homeInfo?.image }} className="w-screen h-40" />
      {/* <Image source={{ uri: getStrapiMedia(homeInfo?.image) }} className="w-screen h-40" /> */}
      <View className={`w-screen p-4 ${colorScheme === 'dark' ? 'text-white' : ''}`}>
          {blockContent && 
          <BlocksRenderer
            content={blockContent}
            // blocks={{
            //   image: ({ image }) => (
            //     <Image
            //       source={{ uri: getStrapiMedia(image.url) }}
            //       style={{ width: image.width ?? 300, height: image.height ?? 300 }}
            //       resizeMode="cover"
            //     />
            //   ),
            // }}
          />}
      </View>
=================
Committed code: Used SG approach to fix image not showing in index page in Android app

===============
===============

Comments