feat(mobile): show OTA update id next to app version
Meine Tiere tab footer now shows 'Version X.X.X - OTA <short-id>' (or 'Build (kein OTA)' when running the embedded/non-OTA bundle) via expo-updates' Updates.isEmbeddedLaunch/updateId. Directly answers the support question 'did the update actually arrive on this device?' by comparing the shown short id against the 'Android update ID' an 'eas update' publish prints - the exact ambiguity that cost a multi-day debugging session earlier (see .claude/handoffs/2026-09-12-ota-update- mystery-expo-dev-client-root-cause.md). Code-reviewed before commit (per WORKFLOW.md step 6): fixed two of four findings - wrapped the expo-updates reads in a try/catch (LegalSection sits outside FooterErrorBoundary, so an unguarded throw here would have taken down the whole footer) and moved the hardcoded 'OTA ' prefix into i18n (pets.otaId). The web-shim-always-unknown finding is a documented dead branch (this app ships no web build - eas update only ever runs --platform android). No test coverage added - no RN test infrastructure exists in mobile/ yet (pre-existing gap, tracked in WORKFLOW.md). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7187fedb38
commit
8b1aba1d9f
3 files changed
+40
-4
No files matched your search
@@ -4,6 +4,7 @@ import { useRouter } from "expo-router";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import Constants from "expo-constants";
|
||||
import * as Updates from "expo-updates";
|
||||
import * as Sentry from "@sentry/react-native";
|
||||
import { useAuth } from "@clerk/expo";
|
||||
import { Check, Sun, Moon, SunMoon } from "lucide-react-native";
|
||||
@@ -216,12 +217,41 @@ function PetSuggestionsSection() {
|
||||
);
|
||||
}
|
||||
|
||||
/** Links to the fixed-language legal pages (see root CLAUDE.md: not localized, always German) plus the installed app version, for anyone who needs to reach them without going through the web app. */
|
||||
/**
|
||||
* Reads expo-updates' launch info defensively — these are plain native-module-backed exports, not
|
||||
* function calls, but a stripped/misconfigured build (e.g. a dev client without the module properly
|
||||
* linked) throwing here would otherwise crash LegalSection's render, and LegalSection sits outside
|
||||
* FooterErrorBoundary (that only wraps LogoutSection below it), so there's no fallback UI to catch it.
|
||||
* Not meaningful on the web target (the web shim always reports isEmbeddedLaunch=false/updateId=null) —
|
||||
* this app has no shipped web build (`eas update` only ever runs `--platform android`, see README), so
|
||||
* that's a dead branch today, not a live bug.
|
||||
*/
|
||||
function readOtaUpdateId(): { isEmbeddedLaunch: boolean; updateId: string | null } {
|
||||
try {
|
||||
return { isEmbeddedLaunch: Updates.isEmbeddedLaunch, updateId: Updates.updateId };
|
||||
} catch {
|
||||
return { isEmbeddedLaunch: false, updateId: null };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Links to the fixed-language legal pages (see root CLAUDE.md: not localized, always German) plus
|
||||
* the installed app version and current OTA update state, for anyone who needs to reach them without
|
||||
* going through the web app. The OTA suffix exists so a support conversation ("did the update actually
|
||||
* arrive?") can be answered by reading this screen instead of guessing — compare the short id shown
|
||||
* here against the "Android update ID" an `eas update` publish prints.
|
||||
*/
|
||||
function LegalSection() {
|
||||
const { t } = useTranslation();
|
||||
const { colors } = useTheme();
|
||||
const styles = useMemo(() => makeStyles(colors), [colors]);
|
||||
const version = Constants.expoConfig?.version ?? "?";
|
||||
const { isEmbeddedLaunch, updateId } = readOtaUpdateId();
|
||||
const ota = isEmbeddedLaunch
|
||||
? t("pets.otaEmbedded")
|
||||
: updateId
|
||||
? t("pets.otaId", { id: updateId.slice(0, 8) })
|
||||
: t("pets.otaUnknown");
|
||||
|
||||
const links = [
|
||||
{ label: t("pets.legalImpressum"), path: "impressum" },
|
||||
@@ -237,7 +267,7 @@ function LegalSection() {
|
||||
<Text style={styles.legalLink}>{link.label}</Text>
|
||||
</Pressable>
|
||||
))}
|
||||
<Text style={styles.versionText}>{t("pets.versionLabel", { version })}</Text>
|
||||
<Text style={styles.versionText}>{t("pets.versionLabel", { version, ota })}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -209,7 +209,10 @@
|
||||
"legalChildSafety": "Kinderschutz",
|
||||
"petSuggestionsTitle": "Tier-Vorschläge",
|
||||
"petSuggestionsLabel": "Erhalte gelegentlich eine Benachrichtigung zu einem Tier, das dir gefallen könnte.",
|
||||
"versionLabel": "Version {{version}}",
|
||||
"versionLabel": "Version {{version}} - {{ota}}",
|
||||
"otaId": "OTA {{id}}",
|
||||
"otaEmbedded": "Build (kein OTA)",
|
||||
"otaUnknown": "OTA unbekannt",
|
||||
"logoutButton": "Abmelden",
|
||||
"logoutConfirmTitle": "Abmelden?",
|
||||
"logoutConfirmMessage": "Du wirst von deinem Konto abgemeldet und zum Login zurückgeschickt."
|
||||
|
||||
@@ -209,7 +209,10 @@
|
||||
"legalChildSafety": "Child Safety Standards",
|
||||
"petSuggestionsTitle": "Pet suggestions",
|
||||
"petSuggestionsLabel": "Occasionally get notified about a pet you might like.",
|
||||
"versionLabel": "Version {{version}}",
|
||||
"versionLabel": "Version {{version}} - {{ota}}",
|
||||
"otaId": "OTA {{id}}",
|
||||
"otaEmbedded": "Build (no OTA)",
|
||||
"otaUnknown": "OTA unknown",
|
||||
"logoutButton": "Log out",
|
||||
"logoutConfirmTitle": "Log out?",
|
||||
"logoutConfirmMessage": "You'll be signed out of your account and sent back to the login screen."
|
||||
|
||||
Reference in new issue
Block a user