push notifications not working for some reason
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
GITEA_URL,
|
||||
GITEA_OWNER,
|
||||
GITEA_REPO,
|
||||
RELEASE_TAG_PREFIX,
|
||||
UPDATE_CHECK_TIMEOUT_MS,
|
||||
} from '../config/gitea';
|
||||
import { version as currentVersion } from '../version.json';
|
||||
|
||||
export interface UpdateInfo {
|
||||
/** Build number of the latest release (same unit as currentVersion). */
|
||||
latestVersion: number;
|
||||
/** Direct URL the user can open to read the release notes / download the APK. */
|
||||
releaseUrl: string;
|
||||
/** Tag name as stored on Gitea, e.g. "mobile-v42". */
|
||||
tagName: string;
|
||||
}
|
||||
|
||||
export interface UseUpdateCheckResult {
|
||||
/** True while the API call is in flight. */
|
||||
checking: boolean;
|
||||
/** Populated once the check completes and a newer build exists. */
|
||||
updateInfo: UpdateInfo | null;
|
||||
/** Non-null when the check failed (network error, timeout, bad response). */
|
||||
error: Error | null;
|
||||
/** Re-run the check manually (e.g. from a "check again" button). */
|
||||
recheck: () => void;
|
||||
}
|
||||
|
||||
// ─── Gitea releases API ──────────────────────────────────────────────────────
|
||||
|
||||
interface GiteaRelease {
|
||||
id: number;
|
||||
tag_name: string;
|
||||
name: string;
|
||||
html_url: string;
|
||||
draft: boolean;
|
||||
prerelease: boolean;
|
||||
}
|
||||
|
||||
async function fetchLatestRelease(): Promise<GiteaRelease> {
|
||||
const url = `${GITEA_URL}/api/v1/repos/${GITEA_OWNER}/${GITEA_REPO}/releases/latest`;
|
||||
|
||||
const controller = new AbortController();
|
||||
const timer = setTimeout(() => controller.abort(), UPDATE_CHECK_TIMEOUT_MS);
|
||||
|
||||
try {
|
||||
const res = await fetch(url, { signal: controller.signal });
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`Gitea API returned HTTP ${res.status}`);
|
||||
}
|
||||
|
||||
return (await res.json()) as GiteaRelease;
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
|
||||
/** Parse the integer build number out of a tag like "mobile-v42". Returns NaN if it can't. */
|
||||
function parseBuildNumber(tagName: string): number {
|
||||
if (!tagName.startsWith(RELEASE_TAG_PREFIX)) return NaN;
|
||||
return parseInt(tagName.slice(RELEASE_TAG_PREFIX.length), 10);
|
||||
}
|
||||
|
||||
|
||||
export function useUpdatecheck(): UseUpdateCheckResult {
|
||||
const [checking, setChecking] = useState(false);
|
||||
const [updateInfo, setUpdateInfo] = useState<UpdateInfo | null>(null);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
// A counter we bump to re-trigger the effect without changing the dep array shape.
|
||||
const [trigger, setTrigger] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
async function check() {
|
||||
setChecking(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const release = await fetchLatestRelease();
|
||||
|
||||
if (cancelled) return;
|
||||
|
||||
// Skip drafts and pre-releases.
|
||||
if (release.draft || release.prerelease) {
|
||||
setUpdateInfo(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const latestVersion = parseBuildNumber(release.tag_name);
|
||||
|
||||
if (isNaN(latestVersion)) {
|
||||
// The tag doesn't follow our scheme — ignore it silently.
|
||||
setUpdateInfo(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (latestVersion > currentVersion) {
|
||||
setUpdateInfo({
|
||||
latestVersion,
|
||||
releaseUrl: release.html_url,
|
||||
tagName: release.tag_name,
|
||||
});
|
||||
} else {
|
||||
setUpdateInfo(null);
|
||||
}
|
||||
} catch (err) {
|
||||
if (cancelled) return;
|
||||
// Don't surface AbortError as a real error — it's just a timeout.
|
||||
if (err instanceof Error && err.name === 'AbortError') {
|
||||
setError(new Error('Update check timed out. Check your connection.'));
|
||||
} else {
|
||||
setError(err instanceof Error ? err : new Error(String(err)));
|
||||
}
|
||||
} finally {
|
||||
if (!cancelled) setChecking(false);
|
||||
}
|
||||
}
|
||||
|
||||
check();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [trigger]);
|
||||
|
||||
return {
|
||||
checking,
|
||||
updateInfo,
|
||||
error,
|
||||
recheck: () => setTrigger(t => t + 1),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user