diff --git a/README.md b/README.md
index 908d02c..e8ca9e3 100644
--- a/README.md
+++ b/README.md
@@ -193,7 +193,15 @@ EXPO_PUBLIC_GEOAPIFY_API_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- Go to the [Google Cloud Console](https://console.cloud.google.com/).
- Create a new project (or use an existing one).
-- Navigate to the "APIs & Services" section and enable the required APIs (Places API, Directions API).
+- Navigate to the "APIs & Services" section and enable **all** of the required APIs:
+ - **Maps SDK for Android** — draws the map itself. Without it Android renders an
+ empty grey tile area and logcat shows an authorization failure; the app looks
+ like the map "didn't load."
+ - **Maps SDK for iOS** — only needed if `components/map.tsx` is switched from
+ `PROVIDER_DEFAULT` (Apple Maps) to `PROVIDER_GOOGLE`.
+ - **Places API (New)** — destination search in `components/google-text-input.tsx`.
+ The legacy Places web service is unavailable to newer Google Cloud projects.
+ - **Directions API** — route polyline and driver ETAs.
- Go to the "Credentials" tab and click on "Create Credentials."
- Select "API Key."
- Copy the generated **API Key** and add it to your `.env` file:
diff --git a/app.config.js b/app.config.js
index fbfa41d..f8b82e3 100644
--- a/app.config.js
+++ b/app.config.js
@@ -12,9 +12,22 @@ const LOCATION_PERMISSION =
module.exports = ({ config }) => {
if (!googleMapsApiKey) {
- console.warn(
- "[app.config] EXPO_PUBLIC_GOOGLE_API_KEY is not set — maps will render blank on Android.",
- );
+ const message =
+ "EXPO_PUBLIC_GOOGLE_API_KEY is not set — the Android manifest will have no " +
+ "Maps key and the map will render blank.";
+
+ // `.env` is gitignored, so it is never uploaded to EAS. Unless the key is
+ // also registered as an EAS environment variable the build silently
+ // produces a keyless APK, and the blank map only shows up on the device.
+ // Fail the build here rather than shipping that.
+ if (process.env.EAS_BUILD) {
+ throw new Error(
+ `[app.config] ${message} Register it with \`eas env:create\` (or in the ` +
+ "EAS dashboard) for this build profile.",
+ );
+ }
+
+ console.warn(`[app.config] ${message}`);
}
return {
diff --git a/app/(root)/(tabs)/home.tsx b/app/(root)/(tabs)/home.tsx
index 3f066e9..60142ff 100644
--- a/app/(root)/(tabs)/home.tsx
+++ b/app/(root)/(tabs)/home.tsx
@@ -47,7 +47,6 @@ const Home = () => {
router.push("/(root)/find-ride");
};
-
return (
{
contentContainerStyle={{
paddingBottom: 100,
}}
- ListEmptyComponent={() => (
+ ListEmptyComponent={
{!loading ? (
<>
@@ -74,16 +73,21 @@ const Home = () => {
)}
- )}
- ListHeaderComponent={() => (
+ }
+ // Passed as an *element*, not as `() => (...)`. VirtualizedList
+ // renders a function prop as ``, so a fresh arrow
+ // function each render is a fresh element type: React unmounts the
+ // whole header — MapView included — and mounts a new one. Recreating
+ // the Android map surface on every re-render leaves it grey with the
+ // Google logo and tiles that never finish loading.
+ ListHeaderComponent={
<>
- Welcome{" "}
- {user?.name || user?.email} 👋
+ Welcome {user?.name || user?.email} 👋
@@ -140,7 +144,7 @@ const Home = () => {
Recent Rides
>
- )}
+ }
/>
);
diff --git a/app/(root)/(tabs)/rides.tsx b/app/(root)/(tabs)/rides.tsx
index a0adbb3..8275366 100644
--- a/app/(root)/(tabs)/rides.tsx
+++ b/app/(root)/(tabs)/rides.tsx
@@ -23,7 +23,7 @@ const Rides = () => {
contentContainerStyle={{
paddingBottom: 100,
}}
- ListEmptyComponent={() => (
+ ListEmptyComponent={
{!loading ? (
<>
@@ -39,12 +39,10 @@ const Rides = () => {
)}
- )}
- ListHeaderComponent={() => (
- <>
- All rides
- >
- )}
+ }
+ ListHeaderComponent={
+ All rides
+ }
/>
);
diff --git a/app/(root)/confirm-ride.tsx b/app/(root)/confirm-ride.tsx
index ebaefd0..b8c7793 100644
--- a/app/(root)/confirm-ride.tsx
+++ b/app/(root)/confirm-ride.tsx
@@ -20,13 +20,13 @@ const ConfirmRide = () => {
setSelected={() => setSelectedDriver(item.id)}
/>
)}
- ListEmptyComponent={() => (
+ ListEmptyComponent={
No drivers available on this route right now.{"\n"}Please try
another destination.
- )}
- ListFooterComponent={() => (
+ }
+ ListFooterComponent={
{
className={selectedDriver === null ? "opacity-50" : ""}
/>
- )}
+ }
/>
);
diff --git a/package-lock.json b/package-lock.json
index 28f0dd3..d1069dc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -29,7 +29,7 @@
"expo-auth-session": "~5.5.2",
"expo-clipboard": "~6.0.3",
"expo-constants": "~16.0.2",
- "expo-crypto": "^57.0.1",
+ "expo-crypto": "~13.0.2",
"expo-font": "~12.0.9",
"expo-linking": "^6.3.1",
"expo-location": "^17.0.1",
@@ -9337,17 +9337,6 @@
"invariant": "^2.2.4"
}
},
- "node_modules/expo-auth-session/node_modules/expo-crypto": {
- "version": "13.0.2",
- "resolved": "https://registry.npmjs.org/expo-crypto/-/expo-crypto-13.0.2.tgz",
- "integrity": "sha512-7f/IMPYJZkBM21LNEMXGrNo/0uXSVfZTwufUdpNKedJR0fm5fH4DCSN79ZddlV26nF90PuXjK2inIbI6lb0qRA==",
- "dependencies": {
- "base64-js": "^1.3.0"
- },
- "peerDependencies": {
- "expo": "*"
- }
- },
"node_modules/expo-clipboard": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/expo-clipboard/-/expo-clipboard-6.0.3.tgz",
@@ -9368,9 +9357,12 @@
}
},
"node_modules/expo-crypto": {
- "version": "57.0.1",
- "resolved": "https://registry.npmjs.org/expo-crypto/-/expo-crypto-57.0.1.tgz",
- "integrity": "sha512-xwegXQw3ATgeL1ZuqbSNrGzOeG+zNeh6Z6DSJk825Qpa3TEQQ1kG3ioE1p3g/SNF373BAVz2iBKUTSytlIbBRA==",
+ "version": "13.0.2",
+ "resolved": "https://registry.npmjs.org/expo-crypto/-/expo-crypto-13.0.2.tgz",
+ "integrity": "sha512-7f/IMPYJZkBM21LNEMXGrNo/0uXSVfZTwufUdpNKedJR0fm5fH4DCSN79ZddlV26nF90PuXjK2inIbI6lb0qRA==",
+ "dependencies": {
+ "base64-js": "^1.3.0"
+ },
"peerDependencies": {
"expo": "*"
}
diff --git a/package.json b/package.json
index fc040cc..9d4b88b 100644
--- a/package.json
+++ b/package.json
@@ -80,7 +80,7 @@
"expo-auth-session": "~5.5.2",
"expo-clipboard": "~6.0.3",
"expo-constants": "~16.0.2",
- "expo-crypto": "^57.0.1",
+ "expo-crypto": "~13.0.2",
"expo-font": "~12.0.9",
"expo-linking": "^6.3.1",
"expo-location": "^17.0.1",