mirror of
https://github.com/ranaldsgift/KefinTweaks.git
synced 2026-11-05 15:52:47 +01:00
Move Update prompt into kefinTweaks-plugin.js
This commit is contained in:
1 parent
6dce194a85
commit
add88f7695
2 files changed
+331
-327
No files matched your search
+2
-327
@@ -1560,344 +1560,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeReleaseTag(tag) {
|
||||
const t = String(tag || '').trim();
|
||||
if (!t) return '';
|
||||
return t.startsWith('v') || t.startsWith('V') ? `v${t.slice(1)}` : `v${t}`;
|
||||
}
|
||||
|
||||
function normalizeUpdateState(config) {
|
||||
if (!config || typeof config !== 'object') return { dismissed: true, latestVersion: '' };
|
||||
if (!config.updateState || typeof config.updateState !== 'object') {
|
||||
config.updateState = { dismissed: true, latestVersion: '' };
|
||||
} else {
|
||||
if (typeof config.updateState.dismissed !== 'boolean') {
|
||||
config.updateState.dismissed = true;
|
||||
}
|
||||
if (typeof config.updateState.latestVersion !== 'string') {
|
||||
config.updateState.latestVersion = '';
|
||||
} else {
|
||||
config.updateState.latestVersion = normalizeReleaseTag(config.updateState.latestVersion)
|
||||
|| config.updateState.latestVersion;
|
||||
}
|
||||
}
|
||||
return config.updateState;
|
||||
}
|
||||
|
||||
function isGithubJsdelivrKefinRoot(root) {
|
||||
return /cdn\.jsdelivr\.net\/gh\/ranaldsgift\/KefinTweaks@/i.test(String(root || ''));
|
||||
}
|
||||
|
||||
function isSymbolicLatestRoot(root) {
|
||||
return /@latest(\/|$)/i.test(String(root || ''));
|
||||
}
|
||||
|
||||
function parseGithubRootRef(root) {
|
||||
const s = String(root || '');
|
||||
const floating = s.match(/@(latest|main|master|experimental)(\/|$)/i);
|
||||
if (floating) {
|
||||
const name = floating[1].toLowerCase();
|
||||
return { kind: name === 'master' ? 'main' : name, value: name === 'master' ? 'main' : name };
|
||||
}
|
||||
const semver = s.match(/@(v?\d+\.\d+\.\d+)(\/|$)/i);
|
||||
if (semver) {
|
||||
return { kind: 'semver', value: normalizeReleaseTag(semver[1]) };
|
||||
}
|
||||
const sha = s.match(/@([0-9a-f]{40})(\/|$)/i);
|
||||
if (sha) {
|
||||
return { kind: 'sha', value: sha[1].toLowerCase() };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function parseShaFromRoot(root) {
|
||||
const m = String(root || '').match(/@([0-9a-f]{40})(\/|$)/i);
|
||||
return m ? m[1].toLowerCase() : null;
|
||||
}
|
||||
|
||||
async function waitForGlobal(getter, timeoutMs = 15000, intervalMs = 250) {
|
||||
const start = Date.now();
|
||||
while (Date.now() - start < timeoutMs) {
|
||||
try {
|
||||
const value = getter();
|
||||
if (value) return value;
|
||||
} catch (_) { /* ignore */ }
|
||||
await new Promise((r) => setTimeout(r, intervalMs));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function persistConfigFromStartup(config) {
|
||||
const saveFn = await waitForGlobal(
|
||||
() => window.KefinTweaksUtils?.saveConfigToJavaScriptInjector,
|
||||
20000
|
||||
);
|
||||
if (!saveFn) {
|
||||
console.warn('[KefinTweaks Startup] saveConfigToJavaScriptInjector unavailable; updateState not persisted');
|
||||
return false;
|
||||
}
|
||||
window.KefinTweaksConfig = config;
|
||||
try {
|
||||
return !!(await saveFn(config));
|
||||
} catch (e) {
|
||||
console.warn('[KefinTweaks Startup] Failed to persist config:', e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchGithubJson(url) {
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) return null;
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async function fetchLatestGithubRelease() {
|
||||
const data = await fetchGithubJson('https://api.github.com/repos/ranaldsgift/KefinTweaks/releases/latest');
|
||||
if (!data?.tag_name) return null;
|
||||
const publishedAt = data.published_at ? new Date(data.published_at) : null;
|
||||
if (!publishedAt || Number.isNaN(publishedAt.getTime())) return null;
|
||||
return {
|
||||
tag: normalizeReleaseTag(data.tag_name),
|
||||
publishedAt
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchReleasePublishedAt(tag) {
|
||||
const normalized = normalizeReleaseTag(tag);
|
||||
if (!normalized) return null;
|
||||
const data = await fetchGithubJson(
|
||||
`https://api.github.com/repos/ranaldsgift/KefinTweaks/releases/tags/${encodeURIComponent(normalized)}`
|
||||
);
|
||||
if (!data?.published_at) return null;
|
||||
const d = new Date(data.published_at);
|
||||
return Number.isNaN(d.getTime()) ? null : d;
|
||||
}
|
||||
|
||||
async function fetchCommitDate(shaOrRef) {
|
||||
if (!shaOrRef) return null;
|
||||
const data = await fetchGithubJson(
|
||||
`https://api.github.com/repos/ranaldsgift/KefinTweaks/commits/${encodeURIComponent(shaOrRef)}`
|
||||
);
|
||||
const iso = data?.commit?.committer?.date || data?.commit?.author?.date;
|
||||
if (!iso) return null;
|
||||
const d = new Date(iso);
|
||||
return Number.isNaN(d.getTime()) ? null : d;
|
||||
}
|
||||
|
||||
async function resolveInstalledCommitDate(root, config, branchName) {
|
||||
const resolvedSha = parseShaFromRoot(config?.kefinTweaksRootResolved)
|
||||
|| parseShaFromRoot(resolvedRootCache);
|
||||
if (resolvedSha) {
|
||||
const fromResolved = await fetchCommitDate(resolvedSha);
|
||||
if (fromResolved) return fromResolved;
|
||||
}
|
||||
return fetchCommitDate(branchName || 'main');
|
||||
}
|
||||
|
||||
/**
|
||||
* True when the install should treat GitHub Latest as a "new update" vs current pin/tip.
|
||||
*/
|
||||
async function isGithubInstallBehindLatest(root, config, latest) {
|
||||
const ref = parseGithubRootRef(root);
|
||||
if (!ref) return true;
|
||||
|
||||
if (ref.kind === 'semver') {
|
||||
if (ref.value === latest.tag) return false;
|
||||
const installedPublished = await fetchReleasePublishedAt(ref.value);
|
||||
if (!installedPublished) return true;
|
||||
return installedPublished.getTime() < latest.publishedAt.getTime();
|
||||
}
|
||||
|
||||
if (ref.kind === 'sha') {
|
||||
const commitDate = await fetchCommitDate(ref.value);
|
||||
if (!commitDate) return true;
|
||||
return commitDate.getTime() < latest.publishedAt.getTime();
|
||||
}
|
||||
|
||||
if (ref.kind === 'main' || ref.kind === 'experimental') {
|
||||
const tipDate = await resolveInstalledCommitDate(root, config, ref.kind);
|
||||
if (!tipDate) return true;
|
||||
// Tip newer than Latest ⇒ following branch tip, not a release-update prompt
|
||||
if (tipDate.getTime() >= latest.publishedAt.getTime()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function buildConcreteLatestRoot(latestTag) {
|
||||
const tag = normalizeReleaseTag(latestTag);
|
||||
return `https://cdn.jsdelivr.net/gh/ranaldsgift/KefinTweaks@${tag}/`;
|
||||
}
|
||||
|
||||
async function showPinnedVersionUpdateToast(config, latestTag) {
|
||||
const toastFn = await waitForGlobal(() => window.KefinTweaksToaster?.toast, 20000);
|
||||
if (!toastFn) {
|
||||
console.warn('[KefinTweaks Startup] Toaster unavailable; skipping update toast');
|
||||
return;
|
||||
}
|
||||
|
||||
const tag = normalizeReleaseTag(latestTag);
|
||||
const githubInstall = isGithubJsdelivrKefinRoot(config?.kefinTweaksRoot);
|
||||
|
||||
const dismissAndSave = async () => {
|
||||
normalizeUpdateState(config);
|
||||
config.updateState.dismissed = true;
|
||||
if (tag) config.updateState.latestVersion = tag;
|
||||
await persistConfigFromStartup(config);
|
||||
};
|
||||
|
||||
const handle = toastFn(
|
||||
'An update for KefinTweaks is available.',
|
||||
'0',
|
||||
false,
|
||||
{
|
||||
buttons: [
|
||||
{
|
||||
label: 'Update',
|
||||
onClick: async () => {
|
||||
try {
|
||||
normalizeUpdateState(config);
|
||||
config.updateState.dismissed = true;
|
||||
if (tag) config.updateState.latestVersion = tag;
|
||||
|
||||
if (githubInstall && tag) {
|
||||
const nextRoot = buildConcreteLatestRoot(tag);
|
||||
config.kefinTweaksRoot = nextRoot;
|
||||
config.kefinTweaksRootResolved = nextRoot;
|
||||
await persistConfigFromStartup(config);
|
||||
handle?.dismiss?.();
|
||||
toastFn(
|
||||
'KefinTweaks updated. Please refresh the page for the changes to take effect.',
|
||||
'8'
|
||||
);
|
||||
} else {
|
||||
await persistConfigFromStartup(config);
|
||||
handle?.dismiss?.();
|
||||
const url = tag
|
||||
? `https://github.com/ranaldsgift/KefinTweaks/releases/tag/${encodeURIComponent(tag)}`
|
||||
: 'https://github.com/ranaldsgift/KefinTweaks/releases/latest';
|
||||
try {
|
||||
window.open(url, '_blank', 'noopener');
|
||||
} catch (_) { /* ignore */ }
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[KefinTweaks Startup] Update action failed:', e);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Dismiss',
|
||||
onClick: async () => {
|
||||
try {
|
||||
await dismissAndSave();
|
||||
handle?.dismiss?.();
|
||||
} catch (e) {
|
||||
console.error('[KefinTweaks Startup] Dismiss action failed:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function checkPinnedVersionUpdate() {
|
||||
const config = window.KefinTweaksConfig;
|
||||
const root = config?.kefinTweaksRoot;
|
||||
if (!root) return;
|
||||
|
||||
// Floating @latest already auto-resolves to GitHub Latest — skip this prompt flow
|
||||
if (isSymbolicLatestRoot(root)) {
|
||||
normalizeUpdateState(config);
|
||||
return;
|
||||
}
|
||||
|
||||
const state = normalizeUpdateState(config);
|
||||
let latest;
|
||||
try {
|
||||
latest = await fetchLatestGithubRelease();
|
||||
} catch (e) {
|
||||
console.warn('[KefinTweaks Startup] Failed to fetch latest release:', e);
|
||||
return;
|
||||
}
|
||||
if (!latest?.tag) return;
|
||||
|
||||
const known = normalizeReleaseTag(state.latestVersion);
|
||||
if (known !== latest.tag) {
|
||||
let markNew = false;
|
||||
if (isGithubJsdelivrKefinRoot(root)) {
|
||||
try {
|
||||
markNew = await isGithubInstallBehindLatest(root, config, latest);
|
||||
} catch (e) {
|
||||
console.warn('[KefinTweaks Startup] Install-vs-latest compare failed; treating as new:', e);
|
||||
markNew = true;
|
||||
}
|
||||
} else {
|
||||
// Self-hosted / custom root
|
||||
markNew = true;
|
||||
}
|
||||
|
||||
if (markNew) {
|
||||
state.dismissed = false;
|
||||
state.latestVersion = latest.tag;
|
||||
await persistConfigFromStartup(config);
|
||||
} else {
|
||||
// Same or newer install relative to Latest — remember tag without prompting
|
||||
state.latestVersion = latest.tag;
|
||||
await persistConfigFromStartup(config);
|
||||
}
|
||||
}
|
||||
|
||||
if (!state.dismissed) {
|
||||
await showPinnedVersionUpdateToast(config, latest.tag);
|
||||
}
|
||||
}
|
||||
|
||||
// Startup task - runs only for admin users
|
||||
async function startupTask() {
|
||||
console.log('[KefinTweaks Startup] Starting startup task...');
|
||||
|
||||
// Check if kefinTweaksRoot is configured - startup tasks only run after installation
|
||||
if (!window.KefinTweaksConfig?.kefinTweaksRoot || window.KefinTweaksConfig.kefinTweaksRoot === '') {
|
||||
console.log('[KefinTweaks Startup] kefinTweaksRoot is not configured, skipping startup tasks');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if user is admin (wait up to 5s for login)
|
||||
const isAdmin = await checkAdminWithTimeout(5000);
|
||||
|
||||
if (!isAdmin) {
|
||||
console.log('[KefinTweaks Startup] User is not admin or not logged in, skipping startup task');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Fire-and-forget: remove leftover Watchlist Custom Tab (best-effort)
|
||||
//removeWatchlistFromCustomTabPlugin();
|
||||
|
||||
await checkPinnedVersionUpdate();
|
||||
|
||||
console.log('[KefinTweaks Startup] Startup task completed successfully');
|
||||
} catch (error) {
|
||||
console.error('[KefinTweaks Startup] Error in startup task:', error);
|
||||
}
|
||||
}
|
||||
// Latest release update check lives in kefinTweaks-plugin.js (runs on bake-attached
|
||||
// admin tip-sync as well as live-inject paths). Do not gate it on injector.js load.
|
||||
|
||||
// Start initialization when DOM is ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
await injectVersionBadgeCSS();
|
||||
initialize();
|
||||
// Run startup task after a short delay to ensure ApiClient is ready
|
||||
setTimeout(startupTask, 1000);
|
||||
});
|
||||
} else {
|
||||
(async () => {
|
||||
await injectVersionBadgeCSS();
|
||||
initialize();
|
||||
// Run startup task after a short delay to ensure ApiClient is ready
|
||||
setTimeout(startupTask, 1000);
|
||||
})();
|
||||
}
|
||||
|
||||
|
||||
@@ -542,6 +542,331 @@ window.KefinTweaksConfig = ${JSON.stringify(config, null, 2)};`;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Latest release update prompt (admin; runs from plugin so bake-attached path still checks) ---
|
||||
|
||||
function normalizeReleaseTag(tag) {
|
||||
const t = String(tag || '').trim();
|
||||
if (!t) return '';
|
||||
return t.startsWith('v') || t.startsWith('V') ? `v${t.slice(1)}` : `v${t}`;
|
||||
}
|
||||
|
||||
function normalizeUpdateState(config) {
|
||||
if (!config || typeof config !== 'object') return { dismissed: true, latestVersion: '' };
|
||||
if (!config.updateState || typeof config.updateState !== 'object') {
|
||||
config.updateState = { dismissed: true, latestVersion: '' };
|
||||
} else {
|
||||
if (typeof config.updateState.dismissed !== 'boolean') {
|
||||
config.updateState.dismissed = true;
|
||||
}
|
||||
if (typeof config.updateState.latestVersion !== 'string') {
|
||||
config.updateState.latestVersion = '';
|
||||
} else {
|
||||
config.updateState.latestVersion = normalizeReleaseTag(config.updateState.latestVersion)
|
||||
|| config.updateState.latestVersion;
|
||||
}
|
||||
}
|
||||
return config.updateState;
|
||||
}
|
||||
|
||||
function isGithubJsdelivrKefinRoot(root) {
|
||||
return /cdn\.jsdelivr\.net\/gh\/ranaldsgift\/KefinTweaks@/i.test(String(root || ''));
|
||||
}
|
||||
|
||||
function isSymbolicLatestRoot(root) {
|
||||
return /@latest(\/|$)/i.test(String(root || ''));
|
||||
}
|
||||
|
||||
function parseGithubRootRef(root) {
|
||||
const s = String(root || '');
|
||||
const floating = s.match(/@(latest|main|master|experimental)(\/|$)/i);
|
||||
if (floating) {
|
||||
const name = floating[1].toLowerCase();
|
||||
return { kind: name === 'master' ? 'main' : name, value: name === 'master' ? 'main' : name };
|
||||
}
|
||||
const semver = s.match(/@(v?\d+\.\d+\.\d+)(\/|$)/i);
|
||||
if (semver) {
|
||||
return { kind: 'semver', value: normalizeReleaseTag(semver[1]) };
|
||||
}
|
||||
const sha = s.match(/@([0-9a-f]{40})(\/|$)/i);
|
||||
if (sha) {
|
||||
return { kind: 'sha', value: sha[1].toLowerCase() };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function parseShaFromRoot(root) {
|
||||
const m = String(root || '').match(/@([0-9a-f]{40})(\/|$)/i);
|
||||
return m ? m[1].toLowerCase() : null;
|
||||
}
|
||||
|
||||
async function waitForGlobal(getter, timeoutMs = 15000, intervalMs = 250) {
|
||||
const start = Date.now();
|
||||
while (Date.now() - start < timeoutMs) {
|
||||
try {
|
||||
const value = getter();
|
||||
if (value) return value;
|
||||
} catch (_) { /* ignore */ }
|
||||
await new Promise((r) => setTimeout(r, intervalMs));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function persistConfigFromUpdateCheck(config) {
|
||||
window.KefinTweaksConfig = config;
|
||||
try {
|
||||
// Prefer utils when baked/live scripts have loaded; fall back to plugin saver
|
||||
const saveFn = await waitForGlobal(
|
||||
() => window.KefinTweaksUtils?.saveConfigToJavaScriptInjector,
|
||||
8000
|
||||
);
|
||||
if (saveFn) {
|
||||
return !!(await saveFn(config));
|
||||
}
|
||||
return !!(await saveConfigToJavaScriptInjector(config));
|
||||
} catch (e) {
|
||||
console.warn('[KefinTweaks Installer] Failed to persist updateState:', e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchGithubJson(url) {
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) return null;
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async function fetchLatestGithubRelease() {
|
||||
const data = await fetchGithubJson('https://api.github.com/repos/ranaldsgift/KefinTweaks/releases/latest');
|
||||
if (!data?.tag_name) return null;
|
||||
const publishedAt = data.published_at ? new Date(data.published_at) : null;
|
||||
if (!publishedAt || Number.isNaN(publishedAt.getTime())) return null;
|
||||
return {
|
||||
tag: normalizeReleaseTag(data.tag_name),
|
||||
publishedAt
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchReleasePublishedAt(tag) {
|
||||
const normalized = normalizeReleaseTag(tag);
|
||||
if (!normalized) return null;
|
||||
const data = await fetchGithubJson(
|
||||
`https://api.github.com/repos/ranaldsgift/KefinTweaks/releases/tags/${encodeURIComponent(normalized)}`
|
||||
);
|
||||
if (!data?.published_at) return null;
|
||||
const d = new Date(data.published_at);
|
||||
return Number.isNaN(d.getTime()) ? null : d;
|
||||
}
|
||||
|
||||
async function fetchCommitDate(shaOrRef) {
|
||||
if (!shaOrRef) return null;
|
||||
const data = await fetchGithubJson(
|
||||
`https://api.github.com/repos/ranaldsgift/KefinTweaks/commits/${encodeURIComponent(shaOrRef)}`
|
||||
);
|
||||
const iso = data?.commit?.committer?.date || data?.commit?.author?.date;
|
||||
if (!iso) return null;
|
||||
const d = new Date(iso);
|
||||
return Number.isNaN(d.getTime()) ? null : d;
|
||||
}
|
||||
|
||||
async function resolveInstalledCommitDate(config, branchName) {
|
||||
const resolvedSha = parseShaFromRoot(config?.kefinTweaksRootResolved);
|
||||
if (resolvedSha) {
|
||||
const fromResolved = await fetchCommitDate(resolvedSha);
|
||||
if (fromResolved) return fromResolved;
|
||||
}
|
||||
return fetchCommitDate(branchName || 'main');
|
||||
}
|
||||
|
||||
/**
|
||||
* True when the install should treat GitHub Latest as a "new update" vs current pin/tip.
|
||||
*/
|
||||
async function isGithubInstallBehindLatest(root, config, latest) {
|
||||
const ref = parseGithubRootRef(root);
|
||||
if (!ref) return true;
|
||||
|
||||
if (ref.kind === 'semver') {
|
||||
if (ref.value === latest.tag) return false;
|
||||
const installedPublished = await fetchReleasePublishedAt(ref.value);
|
||||
if (!installedPublished) return true;
|
||||
return installedPublished.getTime() < latest.publishedAt.getTime();
|
||||
}
|
||||
|
||||
if (ref.kind === 'sha') {
|
||||
const commitDate = await fetchCommitDate(ref.value);
|
||||
if (!commitDate) return true;
|
||||
return commitDate.getTime() < latest.publishedAt.getTime();
|
||||
}
|
||||
|
||||
if (ref.kind === 'main' || ref.kind === 'experimental') {
|
||||
const tipDate = await resolveInstalledCommitDate(config, ref.kind);
|
||||
if (!tipDate) return true;
|
||||
// Tip newer than Latest ⇒ following branch tip, not a release-update prompt
|
||||
if (tipDate.getTime() >= latest.publishedAt.getTime()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function buildConcreteLatestRoot(latestTag) {
|
||||
const tag = normalizeReleaseTag(latestTag);
|
||||
return `${JSDELIVR_BASE}@${tag}/`;
|
||||
}
|
||||
|
||||
async function showPinnedVersionUpdateToast(config, latestTag) {
|
||||
const toastFn = await waitForGlobal(() => window.KefinTweaksToaster?.toast, 20000);
|
||||
if (!toastFn) {
|
||||
console.warn('[KefinTweaks Installer] Toaster unavailable; skipping update toast');
|
||||
return;
|
||||
}
|
||||
|
||||
const tag = normalizeReleaseTag(latestTag);
|
||||
const githubInstall = isGithubJsdelivrKefinRoot(config?.kefinTweaksRoot);
|
||||
|
||||
const dismissAndSave = async () => {
|
||||
normalizeUpdateState(config);
|
||||
config.updateState.dismissed = true;
|
||||
if (tag) config.updateState.latestVersion = tag;
|
||||
await persistConfigFromUpdateCheck(config);
|
||||
};
|
||||
|
||||
const handle = toastFn(
|
||||
'An update for KefinTweaks is available.',
|
||||
'0',
|
||||
false,
|
||||
{
|
||||
buttons: [
|
||||
{
|
||||
label: 'Update',
|
||||
onClick: async () => {
|
||||
try {
|
||||
normalizeUpdateState(config);
|
||||
config.updateState.dismissed = true;
|
||||
if (tag) config.updateState.latestVersion = tag;
|
||||
|
||||
if (githubInstall && tag) {
|
||||
const nextRoot = buildConcreteLatestRoot(tag);
|
||||
config.kefinTweaksRoot = nextRoot;
|
||||
config.kefinTweaksRootResolved = nextRoot;
|
||||
await persistConfigFromUpdateCheck(config);
|
||||
handle?.dismiss?.();
|
||||
toastFn(
|
||||
'KefinTweaks updated. Please refresh the page for the changes to take effect.',
|
||||
'8'
|
||||
);
|
||||
} else {
|
||||
await persistConfigFromUpdateCheck(config);
|
||||
handle?.dismiss?.();
|
||||
const url = tag
|
||||
? `https://github.com/ranaldsgift/KefinTweaks/releases/tag/${encodeURIComponent(tag)}`
|
||||
: 'https://github.com/ranaldsgift/KefinTweaks/releases/latest';
|
||||
try {
|
||||
window.open(url, '_blank', 'noopener');
|
||||
} catch (_) { /* ignore */ }
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[KefinTweaks Installer] Update action failed:', e);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Dismiss',
|
||||
onClick: async () => {
|
||||
try {
|
||||
await dismissAndSave();
|
||||
handle?.dismiss?.();
|
||||
} catch (e) {
|
||||
console.error('[KefinTweaks Installer] Dismiss action failed:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function checkPinnedVersionUpdate(config) {
|
||||
const cfg = config || window.KefinTweaksConfig;
|
||||
const root = cfg?.kefinTweaksRoot;
|
||||
if (!root) {
|
||||
console.log('[KefinTweaks Installer] Update check skipped: no kefinTweaksRoot');
|
||||
return;
|
||||
}
|
||||
|
||||
// Floating @latest already auto-resolves to GitHub Latest — skip this prompt flow
|
||||
if (isSymbolicLatestRoot(root)) {
|
||||
normalizeUpdateState(cfg);
|
||||
console.log('[KefinTweaks Installer] Update check skipped: symbolic @latest');
|
||||
return;
|
||||
}
|
||||
|
||||
const state = normalizeUpdateState(cfg);
|
||||
let latest;
|
||||
try {
|
||||
latest = await fetchLatestGithubRelease();
|
||||
} catch (e) {
|
||||
console.warn('[KefinTweaks Installer] Failed to fetch latest release:', e);
|
||||
return;
|
||||
}
|
||||
if (!latest?.tag) {
|
||||
console.warn('[KefinTweaks Installer] Update check: no latest release tag');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(
|
||||
'[KefinTweaks Installer] Update check: known=',
|
||||
state.latestVersion || '(none)',
|
||||
'github=',
|
||||
latest.tag,
|
||||
'publishedAt=',
|
||||
latest.publishedAt?.toISOString?.() || latest.publishedAt
|
||||
);
|
||||
|
||||
const known = normalizeReleaseTag(state.latestVersion);
|
||||
if (known !== latest.tag) {
|
||||
let markNew = false;
|
||||
if (isGithubJsdelivrKefinRoot(root)) {
|
||||
try {
|
||||
markNew = await isGithubInstallBehindLatest(root, cfg, latest);
|
||||
} catch (e) {
|
||||
console.warn('[KefinTweaks Installer] Install-vs-latest compare failed; treating as new:', e);
|
||||
markNew = true;
|
||||
}
|
||||
} else {
|
||||
markNew = true;
|
||||
}
|
||||
|
||||
console.log('[KefinTweaks Installer] Update check: markNew=', markNew);
|
||||
|
||||
if (markNew) {
|
||||
state.dismissed = false;
|
||||
state.latestVersion = latest.tag;
|
||||
await persistConfigFromUpdateCheck(cfg);
|
||||
} else {
|
||||
state.latestVersion = latest.tag;
|
||||
await persistConfigFromUpdateCheck(cfg);
|
||||
}
|
||||
}
|
||||
|
||||
if (!state.dismissed) {
|
||||
await showPinnedVersionUpdateToast(cfg, latest.tag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run once per page load from admin plugin paths (including bake-attached tip sync).
|
||||
*/
|
||||
async function runPinnedVersionUpdateCheck(config) {
|
||||
if (window.__kefinUpdateCheckDone) return;
|
||||
if (!(await isAdmin())) return;
|
||||
window.__kefinUpdateCheckDone = true;
|
||||
try {
|
||||
await checkPinnedVersionUpdate(config || window.KefinTweaksConfig);
|
||||
} catch (e) {
|
||||
console.warn('[KefinTweaks Installer] Update check failed:', e);
|
||||
}
|
||||
}
|
||||
|
||||
function isKefinInjectorAttachedOnPage() {
|
||||
return !!(
|
||||
window.KefinTweaksInjectorAttached
|
||||
@@ -2324,6 +2649,8 @@ main.MuiBox-root .customPage.libraryPage:not(.noSecondaryNavPage)[data-kefin-cus
|
||||
}
|
||||
console.log('[KefinTweaks Installer] KefinTweaks-injector attached; admin tip sync without live inject');
|
||||
await handleAdminAttachedBakeTipSync(config);
|
||||
// Latest release check must run here — injector.js is not loaded on this path
|
||||
await runPinnedVersionUpdateCheck(config);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2347,11 +2674,13 @@ main.MuiBox-root .customPage.libraryPage:not(.noSecondaryNavPage)[data-kefin-cus
|
||||
}));
|
||||
} catch (e) { /* ignore */ }
|
||||
console.log('[KefinTweaks Installer] Applied KefinTweaks-injector assets after auto-create');
|
||||
await runPinnedVersionUpdateCheck(config);
|
||||
// Tip advance inside ensure may have scheduled reload; never also load tip injector.js
|
||||
if (window.__kefinTipReloadScheduled) return;
|
||||
return;
|
||||
}
|
||||
console.log('[KefinTweaks Installer] KefinTweaks-injector not available (' + (ensured.reason || 'unknown') + '); loading injector.js');
|
||||
await runPinnedVersionUpdateCheck(config);
|
||||
} else {
|
||||
console.log('[KefinTweaks Installer] KefinTweaks-injector not on page (non-admin); loading injector.js');
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user