From 99a4593d82e0fa1dd1652e0bcdda6604d6bc2b03 Mon Sep 17 00:00:00 2001 From: Bwlok Date: Thu, 30 Jan 2025 17:34:24 +0100 Subject: [PATCH] Add Update checks Currently only working for Themes --- src/ui/addons/install-modal.tsx | 77 +++++++++++++++++++++++- src/ui/settings/developer/index.tsx | 7 +++ src/utilities/compareSemanticVersions.ts | 40 ++++++++++++ 3 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 src/utilities/compareSemanticVersions.ts diff --git a/src/ui/addons/install-modal.tsx b/src/ui/addons/install-modal.tsx index b5b50a7..4b3b3d4 100644 --- a/src/ui/addons/install-modal.tsx +++ b/src/ui/addons/install-modal.tsx @@ -5,12 +5,14 @@ import { useSettingsStore } from '@api/storage'; import { Discord } from '@api/metro/components'; import { showDialog } from '@api/dialogs'; import { SocialLinks } from '@constants'; -import { showToast } from '@api/toasts'; -import { capitalize } from '@utilities'; +import Toasts, { showToast } from '@api/toasts'; import * as managers from '@managers'; import { View } from 'react-native'; import { Strings } from '@api/i18n'; import { Icons } from '@api/assets'; +import * as Managers from "@managers"; +import {createTimeoutSignal} from "@utilities"; +import { compareSemanticVersions, getHighestVersion} from "@utilities/compareSemanticVersions"; interface InstallModalProps { @@ -154,4 +156,75 @@ export function showInstallAlert({ type, ref }: InstallModalProps) { }); } +export async function checkForUpdates() { + Toasts.showToast({ + title: `Checking for Updates`, + duration: 2000 + }); + //Themes are doing weird stuff and sometimes come up multiple times + const existingAssets = [] + for (const theme of window.UNBOUND_THEMES ?? []) { + const {manifest, bundle} = theme; + + if (!(manifest.id in existingAssets)) { + + if (manifest.hasOwnProperty("updates")) { + const origin = manifest.url.split('/'); + origin.pop(); + const updatesURL = new URL(manifest.updates, origin.join('/') + '/'); + + const updateRequest = await fetch(updatesURL, { + cache: 'no-cache', + signal: createTimeoutSignal() + }).catch((error) => { + this.logger.error('Failed to fetch bundle URL:', error.message); + this.emit('install-error', error); + + }); + + if (!updateRequest) return false; + + if (!updateRequest.ok) { + //logger doesnt wanna work :/ + console.error(`Failed to fetch bundle URL (${updateRequest.status}: ${updateRequest.statusText ?? 'No status text.'})`); + continue; + } + + const updates = await updateRequest.json(); + const highestVer = getHighestVersion(updates); + const latestVersionObject = updates.find(v => v.version === highestVer); //Maybe use for later for Patchnotes + + if (compareSemanticVersions(highestVer, manifest.version) > 0) { + showDialog({ + title: manifest.name, + content: `The Theme "${manifest.name}" has an Update: \n${manifest.version}->${highestVer}\nWould you like to update?`, + buttons: [ + { + text: "Update now", + onPress: () => installUpdates(manifest.url, manifest.name) + }, + + ] + }); + } else if (compareSemanticVersions(highestVer, manifest.version) == 0) { + //Check if TS is newer + } + + + } + + } + + + } + +} + +function installUpdates(updates, name) { + Managers.Themes.install(updates).then(r => Toasts.showToast({ + title: `Updated ${name}`, + duration: 2000 + })); +} + export default { InstallInput, InternalInstallInput }; \ No newline at end of file diff --git a/src/ui/settings/developer/index.tsx b/src/ui/settings/developer/index.tsx index d052bda..adce613 100644 --- a/src/ui/settings/developer/index.tsx +++ b/src/ui/settings/developer/index.tsx @@ -1,3 +1,4 @@ +import {checkForUpdates} from "@ui/addons/install-modal"; import { Section, useFormStyles } from '@ui/misc/forms'; import { SettingsKeys, SocialLinks } from '@constants'; import { ScrollView, View, Text } from 'react-native'; @@ -153,6 +154,12 @@ export default function Developer() { })} arrow /> + } + onPress={async () => await checkForUpdates()} + arrow + /> ; diff --git a/src/utilities/compareSemanticVersions.ts b/src/utilities/compareSemanticVersions.ts new file mode 100644 index 0000000..b50698a --- /dev/null +++ b/src/utilities/compareSemanticVersions.ts @@ -0,0 +1,40 @@ +/** + * @description Compares 2 semantic versions. + * @cases + * @> 0 - versionA is newer than versionB + * @< 0 - versionA is older than versionB + * @= 0 - versionA and versionB are the same + * @param {T extends string} versionA + * @param {T extends string} versionB + * @return {number} + */ + +function compareSemanticVersions(versionA: string, versionB: string): number { + const [majorA, minorA, patchA] = versionA.split('.').map(Number); + const [majorB, minorB, patchB] = versionB.split('.').map(Number); + + if (majorA !== majorB) { + return majorA - majorB; + } + + if (minorA !== minorB) { + return minorA - minorB; + } + + return patchA - patchB; +} + +/** + * @description Retrieves the highest version from updates.json. + * @param {Array<{ version: string }>} versionsList - List of objects containing version information. + * @return {string | null} - The highest version found, or null if the list is empty. + */ +function getHighestVersion(versionsList: { version: string }[]): string | null { + if (versionsList.length === 0) return null; + + return versionsList.reduce((highest, current) => + compareSemanticVersions(highest.version, current.version) > 0 ? highest : current + ).version; +} + +export { compareSemanticVersions, getHighestVersion }; \ No newline at end of file