Skip to content

Commit 7c399ea

Browse files
committed
Refactor App component to utilize granular update checks for major, minor, and patch versions; enhance needsUpdate function to support specific update levels and improve version comparison logic in semver.ts.
1 parent 0ae9274 commit 7c399ea

3 files changed

Lines changed: 94 additions & 11 deletions

File tree

example/App.tsx

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,33 @@ import { getLatestVersion, getStoreUrl, needsUpdate, VersionCheck } from "react-
55

66
export default function App() {
77
const [loading, setLoading] = useState(true);
8-
const [updateAvailable, setUpdateAvailable] = useState(false);
98
const [storeUrl, setStoreUrl] = useState<string | null>(null);
109
const [latestVersion, setLatestVersion] = useState<string | null>(null);
10+
const [updateLevel, setUpdateLevel] = useState<"major" | "minor" | "patch" | null>(null);
1111
const [error, setError] = useState<string | null>(null);
1212

13-
// Fetch all async data on mount
1413
useEffect(() => {
1514
const fetchAll = async () => {
1615
try {
17-
const [update, url, latest] = await Promise.all([needsUpdate(), getStoreUrl(), getLatestVersion()]);
18-
setUpdateAvailable(update);
16+
const [url, latest] = await Promise.all([getStoreUrl(), getLatestVersion()]);
1917
setStoreUrl(url);
2018
setLatestVersion(latest);
19+
20+
// Check each level to determine the most specific update type
21+
const isMajor = await needsUpdate({ level: "major" });
22+
if (isMajor) {
23+
setUpdateLevel("major");
24+
} else {
25+
const isMinor = await needsUpdate({ level: "minor" });
26+
if (isMinor) {
27+
setUpdateLevel("minor");
28+
} else {
29+
const isPatch = await needsUpdate({ level: "patch" });
30+
if (isPatch) setUpdateLevel("patch");
31+
}
32+
}
2133
} catch {
22-
setError("App not found in store"); // this is not an error, it's just a fallback
34+
setError("App not found in store");
2335
}
2436
setLoading(false);
2537
};
@@ -46,13 +58,16 @@ export default function App() {
4658
<Text style={styles.label}>Latest: {latestVersion}</Text>
4759
<Text style={styles.label}>Store URL: {storeUrl}</Text>
4860

49-
{updateAvailable && storeUrl && (
61+
{updateLevel && storeUrl && (
5062
<TouchableOpacity onPress={() => Linking.openURL(storeUrl)}>
51-
<Text style={styles.updateText}>Update available — tap to open store</Text>
63+
<Text style={styles.updateText}>
64+
{updateLevel === "major" ? "Major" : updateLevel === "minor" ? "Minor" : "Patch"} update available — tap
65+
to open store
66+
</Text>
5267
</TouchableOpacity>
5368
)}
5469

55-
{!updateAvailable && <Text style={styles.upToDate}>App is up to date</Text>}
70+
{!updateLevel && <Text style={styles.upToDate}>App is up to date</Text>}
5671
</>
5772
)}
5873

package/src/index.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { NitroModules } from "react-native-nitro-modules";
2+
import type { UpdateLevel } from "./semver";
3+
import { compareVersions, isNewerVersion } from "./semver";
24
import type { VersionCheck as VersionCheckType } from "./specs/Version.nitro";
35

46
const VersionCheck = NitroModules.createHybridObject<VersionCheckType>("VersionCheck");
@@ -78,16 +80,28 @@ export const getLatestVersion = () => VersionCheck.getLatestVersion();
7880
/**
7981
* Checks whether an app update is available.
8082
*
81-
* Compares the current version against the latest store version.
83+
* Uses semantic version comparison. By default checks for any version
84+
* increase, but you can filter by granularity:
85+
*
86+
* - `"major"` — only returns `true` for major bumps (1.x → 2.x)
87+
* - `"minor"` — returns `true` for major or minor bumps
88+
* - `"patch"` — returns `true` for any version increase (default)
8289
*
8390
* @example
8491
* ```ts
8592
* if (await needsUpdate()) {
8693
* const url = await getStoreUrl();
8794
* Linking.openURL(url);
8895
* }
96+
*
97+
* // Only prompt for major updates
98+
* const majorUpdate = await needsUpdate({ level: "major" });
8999
* ```
90100
*/
91-
export const needsUpdate = () => VersionCheck.needsUpdate();
101+
export const needsUpdate = async (options?: { level?: UpdateLevel }): Promise<boolean> => {
102+
const latest = await VersionCheck.getLatestVersion();
103+
return isNewerVersion(VersionCheck.version, latest, options?.level ?? "patch");
104+
};
92105

93-
export { VersionCheck };
106+
export { compareVersions, VersionCheck };
107+
export type { UpdateLevel };

package/src/semver.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
export type UpdateLevel = "major" | "minor" | "patch";
2+
3+
type SemVer = [major: number, minor: number, patch: number];
4+
5+
function parseVersion(version: string): SemVer {
6+
const parts = version.split(".");
7+
return [Number(parts[0]) || 0, Number(parts[1]) || 0, Number(parts[2]) || 0];
8+
}
9+
10+
/**
11+
* Compares two version strings.
12+
*
13+
* @returns `-1` if `v1 < v2`, `0` if equal, `1` if `v1 > v2`
14+
*
15+
* @example
16+
* ```ts
17+
* compareVersions("1.2.0", "1.3.0") // -1
18+
* compareVersions("2.0.0", "1.9.9") // 1
19+
* compareVersions("1.0.0", "1.0.0") // 0
20+
* ```
21+
*/
22+
export function compareVersions(v1: string, v2: string): -1 | 0 | 1 {
23+
const a = parseVersion(v1);
24+
const b = parseVersion(v2);
25+
26+
for (let i = 0; i < 3; i++) {
27+
if (a[i]! > b[i]!) return 1;
28+
if (a[i]! < b[i]!) return -1;
29+
}
30+
31+
return 0;
32+
}
33+
34+
/**
35+
* Checks whether `latest` is newer than `current` at the given granularity.
36+
*
37+
* - `"major"` — only returns `true` for major bumps (1.x → 2.x)
38+
* - `"minor"` — returns `true` for major or minor bumps
39+
* - `"patch"` — returns `true` for any version increase (default)
40+
*/
41+
export function isNewerVersion(current: string, latest: string, level: UpdateLevel = "patch"): boolean {
42+
const [curMajor, curMinor, curPatch] = parseVersion(current);
43+
const [latMajor, latMinor, latPatch] = parseVersion(latest);
44+
45+
if (latMajor > curMajor) return true;
46+
if (latMajor < curMajor) return false;
47+
if (level === "major") return false;
48+
49+
if (latMinor > curMinor) return true;
50+
if (latMinor < curMinor) return false;
51+
if (level === "minor") return false;
52+
53+
return latPatch > curPatch;
54+
}

0 commit comments

Comments
 (0)