+
{
+ const url = `https://github.com/${GH_REPO}/releases`;
+ open(url).catch(() => window.open(url, '_blank'));
+ }}
+ >
{(__VERSION_TAG__ || __COMMIT_HASH__) +
(__GIT_CLEAN__ ? '' : '-dirty')}
{doesMatchSettings && (
-
+
{localIp || 'unknown local ip'}
)}
+ {version && (
+
{
+ const url = document.body.classList.contains('windows_nt')
+ ? 'https://slimevr.dev/download'
+ : `https://github.com/${GH_REPO}/releases/latest`;
+ open(url).catch(() => window.open(url, '_blank'));
+ }}
+ >
+
+
+ )}
{
+ localStorage.setItem('lastVersionFound', newVersion);
+ setForceClose(true);
+ };
+ let isVersionNew = false;
+ try {
+ if (newVersion) {
+ isVersionNew = semver.gt(
+ newVersion,
+ localStorage.getItem('lastVersionFound') || 'v0.0.0'
+ );
+ }
+ } catch {
+ console.error('failed to parse new version');
+ }
+
+ return (
+
+
+ <>
+
+
+
+ {l10n.getString('version_update-title', {
+ version: newVersion,
+ })}
+
+
+ {l10n.getString('version_update-description')}
+
+
+
+
+
+
+ >
+
+
+ );
+}
+
+/**
+ * A GitHub release.
+ */
+export interface Release {
+ url: string;
+ html_url: string;
+ assets_url: string;
+ upload_url: string;
+ tarball_url: string | null;
+ zipball_url: string | null;
+ id: number;
+ node_id: string;
+ /**
+ * The name of the tag.
+ */
+ tag_name: string;
+ /**
+ * Specifies the commitish value that determines where the Git tag is created from.
+ */
+ target_commitish: string;
+ name: string | null;
+ body?: string | null;
+ /**
+ * true to create a draft (unpublished) release, false to create a published one.
+ */
+ draft: boolean;
+ /**
+ * Whether to identify the release as a prerelease or a full release.
+ */
+ prerelease: boolean;
+ created_at: string;
+ published_at: string | null;
+ author: SimpleUser;
+ assets: ReleaseAsset[];
+ body_html?: string;
+ body_text?: string;
+ mentions_count?: number;
+ /**
+ * The URL of the release discussion.
+ */
+ discussion_url?: string;
+ reactions?: ReactionRollup;
+ [k: string]: unknown;
+}
+/**
+ * A GitHub user.
+ */
+export interface SimpleUser {
+ name?: string | null;
+ email?: string | null;
+ login: string;
+ id: number;
+ node_id: string;
+ avatar_url: string;
+ gravatar_id: string | null;
+ url: string;
+ html_url: string;
+ followers_url: string;
+ following_url: string;
+ gists_url: string;
+ starred_url: string;
+ subscriptions_url: string;
+ organizations_url: string;
+ repos_url: string;
+ events_url: string;
+ received_events_url: string;
+ type: string;
+ site_admin: boolean;
+ starred_at?: string;
+ [k: string]: unknown;
+}
+/**
+ * Data related to a release.
+ */
+export interface ReleaseAsset {
+ url: string;
+ browser_download_url: string;
+ id: number;
+ node_id: string;
+ /**
+ * The file name of the asset.
+ */
+ name: string;
+ label: string | null;
+ /**
+ * State of the release asset.
+ */
+ state: 'uploaded' | 'open';
+ content_type: string;
+ size: number;
+ download_count: number;
+ created_at: string;
+ updated_at: string;
+ uploader: null | SimpleUser1;
+ [k: string]: unknown;
+}
+/**
+ * A GitHub user.
+ */
+export interface SimpleUser1 {
+ name?: string | null;
+ email?: string | null;
+ login: string;
+ id: number;
+ node_id: string;
+ avatar_url: string;
+ gravatar_id: string | null;
+ url: string;
+ html_url: string;
+ followers_url: string;
+ following_url: string;
+ gists_url: string;
+ starred_url: string;
+ subscriptions_url: string;
+ organizations_url: string;
+ repos_url: string;
+ events_url: string;
+ received_events_url: string;
+ type: string;
+ site_admin: boolean;
+ starred_at?: string;
+ [k: string]: unknown;
+}
+export interface ReactionRollup {
+ url: string;
+ total_count: number;
+ '+1': number;
+ '-1': number;
+ laugh: number;
+ confused: number;
+ heart: number;
+ hooray: number;
+ eyes: number;
+ rocket: number;
+ [k: string]: unknown;
+}
diff --git a/gui/src/components/commons/icon/DownloadIcon.tsx b/gui/src/components/commons/icon/DownloadIcon.tsx
new file mode 100644
index 000000000..c1e7fed14
--- /dev/null
+++ b/gui/src/components/commons/icon/DownloadIcon.tsx
@@ -0,0 +1,16 @@
+export function DownloadIcon({ width = 22 }: { width?: number }) {
+ return (
+
+ );
+}
diff --git a/package-lock.json b/package-lock.json
index 51e165907..e40f09a7a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -45,6 +45,7 @@
"react-hook-form": "^7.29.0",
"react-modal": "3.15.1",
"react-router-dom": "^6.2.2",
+ "semver": "^7.5.0",
"solarxr-protocol": "file:../solarxr-protocol",
"three": "^0.148.0",
"typescript": "^4.6.3"
@@ -8573,9 +8574,9 @@
}
},
"node_modules/semver": {
- "version": "7.3.8",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
- "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz",
+ "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==",
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -15354,9 +15355,9 @@
}
},
"semver": {
- "version": "7.3.8",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
- "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz",
+ "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==",
"requires": {
"lru-cache": "^6.0.0"
}
@@ -15461,6 +15462,7 @@
"react-hook-form": "^7.29.0",
"react-modal": "3.15.1",
"react-router-dom": "^6.2.2",
+ "semver": "^7.5.0",
"solarxr-protocol": "file:../solarxr-protocol",
"tailwind-gradient-mask-image": "^1.0.0",
"tailwindcss": "^3.3.1",