fix(mobile): align OTA provider state

This commit is contained in:
DIYgod 2026-04-10 20:46:17 +08:00
parent 49ec6d818a
commit 56b23b55ee
3 changed files with 92 additions and 21 deletions

View File

@ -41,7 +41,7 @@ export default ({ config }: ConfigContext): ExpoConfig => {
},
owner: "follow",
updates: {
url: "https://folo-custom-expo-updates.vercel.app/api/manifest",
url: "https://ota.folo.is/manifest",
requestHeaders: {
"expo-channel-name": channelName,
},

View File

@ -18,5 +18,41 @@ describe("reduceOtaState", () => {
expect(state.status).toBe("ready")
expect(state.pendingVersion).toBe("0.4.2")
expect(state.errorMessage).toBeNull()
})
it("stores the latest error without clearing the pending version", () => {
const state = reduceOtaState(
{
status: "ready",
pendingVersion: "0.4.2",
errorMessage: null,
},
{
type: "failed",
message: "Network unavailable",
},
)
expect(state.status).toBe("error")
expect(state.pendingVersion).toBe("0.4.2")
expect(state.errorMessage).toBe("Network unavailable")
})
it("resets the OTA state back to idle", () => {
const state = reduceOtaState(
{
status: "error",
pendingVersion: "0.4.2",
errorMessage: "Network unavailable",
},
{
type: "reset",
},
)
expect(state.status).toBe("idle")
expect(state.pendingVersion).toBeNull()
expect(state.errorMessage).toBeNull()
})
})

View File

@ -14,24 +14,13 @@ const resolvePendingVersion = (manifest: Manifest | undefined): string => {
return Updates.runtimeVersion ?? "unknown"
}
const metadataVersion =
const metadataReleaseVersion =
"metadata" in manifest && manifest.metadata && typeof manifest.metadata === "object"
? Reflect.get(manifest.metadata, "version")
? Reflect.get(manifest.metadata, "releaseVersion")
: undefined
if (typeof metadataVersion === "string" && metadataVersion.length > 0) {
return metadataVersion
}
const expoClientVersion =
"extra" in manifest &&
manifest.extra?.expoClient &&
typeof manifest.extra.expoClient.version === "string"
? manifest.extra.expoClient.version
: undefined
if (expoClientVersion) {
return expoClientVersion
if (typeof metadataReleaseVersion === "string" && metadataReleaseVersion.length > 0) {
return metadataReleaseVersion
}
if ("runtimeVersion" in manifest && typeof manifest.runtimeVersion === "string") {
@ -42,10 +31,42 @@ const resolvePendingVersion = (manifest: Manifest | undefined): string => {
}
export const OtaProvider = ({ children }: PropsWithChildren) => {
const { checkError, downloadError, downloadedUpdate, isUpdatePending } = Updates.useUpdates()
const [state, dispatch] = useReducer(reduceOtaState, initialOtaState)
const nativePendingVersion = useMemo(() => {
if (!isUpdatePending || downloadedUpdate?.type !== Updates.UpdateInfoType.NEW) {
return null
}
return resolvePendingVersion(downloadedUpdate.manifest)
}, [downloadedUpdate, isUpdatePending])
useEffect(() => {
if (__DEV__ || !Updates.isEnabled) {
if (!nativePendingVersion) {
return
}
dispatch({
type: "downloaded",
version: nativePendingVersion,
})
}, [nativePendingVersion])
useEffect(() => {
const error = downloadError || checkError
if (!error) {
return
}
dispatch({
type: "failed",
message: error.message,
})
}, [checkError, downloadError])
useEffect(() => {
if (__DEV__ || !Updates.isEnabled || nativePendingVersion) {
return
}
@ -61,7 +82,9 @@ export const OtaProvider = ({ children }: PropsWithChildren) => {
}
if (!checkResult.isAvailable) {
dispatch({ type: "reset" })
if (!isUpdatePending) {
dispatch({ type: "reset" })
}
return
}
@ -79,7 +102,9 @@ export const OtaProvider = ({ children }: PropsWithChildren) => {
return
}
dispatch({ type: "reset" })
if (!isUpdatePending) {
dispatch({ type: "reset" })
}
} catch (error) {
if (isCancelled) {
return
@ -100,9 +125,19 @@ export const OtaProvider = ({ children }: PropsWithChildren) => {
isCancelled = true
task.cancel()
}
}, [])
}, [isUpdatePending, nativePendingVersion])
const value = useMemo(() => state, [state])
const value = useMemo(() => {
if (!nativePendingVersion) {
return state
}
return {
status: "ready" as const,
pendingVersion: nativePendingVersion,
errorMessage: null,
}
}, [nativePendingVersion, state])
return <OtaContext value={value}>{children}</OtaContext>
}