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",