Add Updater UI and deferred plugin updates
Introduce Updater settings (VENDETTA_UPDATER_SETTINGS) and add a new Updater settings page with manual refresh, per-plugin update, and Update All functionality Respect fetchPluginsOnStart and per-repo repoAutoFetchOverrides when fetching plugin repositories to avoid startup network work Stagger plugin initialization and defer repository updates; change the scheduled refresh delay to 5 minutes and add periodic GitHub bundle checks to detect new releases Remove obsolete team array from General page
This commit is contained in:
parent
d4d491fa7a
commit
9f6f62093d
6 changed files with 464 additions and 55 deletions
|
|
@ -20,6 +20,13 @@ export default function initSettings() {
|
|||
render: () => import("@core/ui/settings/pages/General"),
|
||||
useTrailing: () => `(${version})`,
|
||||
},
|
||||
{
|
||||
key: "SHIGGYCORD_UPDATER",
|
||||
title: () => "Updater",
|
||||
icon: findAssetId("BellIcon"),
|
||||
render: () => import("@core/ui/settings/pages/Updater"),
|
||||
},
|
||||
|
||||
{
|
||||
key: "CORE_PLUGINS",
|
||||
title: () => Strings.PLUGINS,
|
||||
|
|
|
|||
|
|
@ -29,25 +29,6 @@ export default function General() {
|
|||
const debugInfo = getDebugInfo();
|
||||
const navigation = NavigationNative.useNavigation();
|
||||
|
||||
// Team members array for easy editing
|
||||
const TEAM_MEMBERS = [
|
||||
{
|
||||
name: "kmmiio99o",
|
||||
role: "Lead Developer",
|
||||
avatar: "https://avatars.githubusercontent.com/u/73043890?v=4",
|
||||
profile: "https://github.com/kmmiio99o",
|
||||
},
|
||||
{
|
||||
name: "Angelw0lf",
|
||||
role: "Contributor",
|
||||
avatar: "https://avatars.githubusercontent.com/u/112412567?v=4",
|
||||
profile: "https://github.com/Angelw0lf",
|
||||
},
|
||||
// Add or remove members here
|
||||
];
|
||||
|
||||
// Handler for About the Team is no longer needed (navigation used instead)
|
||||
|
||||
return (
|
||||
<ScrollView
|
||||
style={{ flex: 1 }}
|
||||
|
|
|
|||
262
src/core/ui/settings/pages/Updater/index.tsx
Normal file
262
src/core/ui/settings/pages/Updater/index.tsx
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { useProxy } from "@core/vendetta/storage";
|
||||
import {
|
||||
updateAllRepository,
|
||||
registeredPlugins,
|
||||
pluginRepositories,
|
||||
isGreaterVersion,
|
||||
refreshPlugin,
|
||||
} from "@lib/addons/plugins";
|
||||
import { updaterSettings } from "@lib/api/settings";
|
||||
import { findAssetId } from "@lib/api/assets";
|
||||
import { getDebugInfo } from "@lib/api/debug";
|
||||
import {
|
||||
Stack,
|
||||
TableRowGroup,
|
||||
TableSwitchRow,
|
||||
TableRow,
|
||||
Text,
|
||||
Button,
|
||||
Card,
|
||||
IconButton,
|
||||
} from "@metro/common/components";
|
||||
import { ScrollView, View } from "react-native";
|
||||
|
||||
/**
|
||||
* Updater UI
|
||||
* - Shows updater toggles (auto update, fetch-on-start)
|
||||
* - Shows bundle info with refresh button
|
||||
* - Lists available plugin updates discovered after refresh
|
||||
* - Provides per-plugin Update button and Update All
|
||||
*/
|
||||
|
||||
type UpdateEntry = {
|
||||
id: string;
|
||||
name: string;
|
||||
current: string;
|
||||
latest: string;
|
||||
repo: string;
|
||||
};
|
||||
|
||||
export default function Updater() {
|
||||
// Bind persisted settings
|
||||
useProxy(updaterSettings);
|
||||
|
||||
const [checking, setChecking] = useState(false);
|
||||
const [available, setAvailable] = useState<UpdateEntry[]>([]);
|
||||
const [lastError, setLastError] = useState<string | null>(null);
|
||||
|
||||
const fetchPluginsOnStart = updaterSettings.fetchPluginsOnStart ?? true;
|
||||
const autoEnabled = !!updaterSettings.autoUpdateEnabled;
|
||||
const lastChecked = updaterSettings.lastChecked ?? null;
|
||||
|
||||
const debugInfo = getDebugInfo();
|
||||
|
||||
useEffect(() => {
|
||||
// Optionally, we could auto-refresh after app fully loads if configured,
|
||||
// but we intentionally keep refresh manual here to avoid background network.
|
||||
}, []);
|
||||
|
||||
const setAutoUpdate = (v: boolean) => {
|
||||
updaterSettings.autoUpdateEnabled = v;
|
||||
};
|
||||
|
||||
const setFetchOnStart = (v: boolean) => {
|
||||
updaterSettings.fetchPluginsOnStart = v;
|
||||
};
|
||||
|
||||
const refresh = async () => {
|
||||
if (checking) return;
|
||||
setChecking(true);
|
||||
setLastError(null);
|
||||
|
||||
try {
|
||||
// Update repositories (this function is aware of updaterSettings)
|
||||
await updateAllRepository();
|
||||
|
||||
// mark last checked
|
||||
updaterSettings.lastChecked = new Date().toISOString();
|
||||
|
||||
// Build available updates list:
|
||||
// Iterate known repositories and compare remote versions with registered plugin versions.
|
||||
const pending: UpdateEntry[] = [];
|
||||
|
||||
const repos = Object.keys(pluginRepositories || {});
|
||||
for (const repoUrl of repos) {
|
||||
const repo = pluginRepositories[repoUrl];
|
||||
if (!repo) continue;
|
||||
|
||||
for (const id of Object.keys(repo)) {
|
||||
if (id === "$meta") continue;
|
||||
const remoteInfo = repo[id];
|
||||
if (!remoteInfo || !remoteInfo.version) continue;
|
||||
|
||||
const registered = registeredPlugins.get(id);
|
||||
if (!registered) continue;
|
||||
|
||||
const currentVersion = (registered as any).version ?? "0.0.0";
|
||||
const latestVersion = remoteInfo.version;
|
||||
|
||||
if (isGreaterVersion(latestVersion, currentVersion)) {
|
||||
pending.push({
|
||||
id,
|
||||
name: (registered as any).display?.name ?? id,
|
||||
current: currentVersion,
|
||||
latest: latestVersion,
|
||||
repo: repoUrl,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setAvailable(pending);
|
||||
} catch (e) {
|
||||
console.error("Updater refresh failed", e);
|
||||
setLastError(String((e as Error)?.message ?? e));
|
||||
} finally {
|
||||
setChecking(false);
|
||||
}
|
||||
};
|
||||
|
||||
const updatePlugin = async (id: string, repo: string) => {
|
||||
try {
|
||||
await refreshPlugin(id, repo);
|
||||
// remove from list
|
||||
setAvailable((prev) => prev.filter((p) => p.id !== id));
|
||||
} catch (e) {
|
||||
console.error(`Failed updating plugin ${id}`, e);
|
||||
setLastError(String((e as Error)?.message ?? e));
|
||||
}
|
||||
};
|
||||
|
||||
const updateAll = async () => {
|
||||
// Iterate copy because we modify available inside loop
|
||||
const items = [...available];
|
||||
for (const it of items) {
|
||||
// sequential to avoid spamming network, but could be parallelized if desired
|
||||
// ignore errors per plugin and continue
|
||||
try {
|
||||
// best-effort
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await updatePlugin(it.id, it.repo);
|
||||
} catch {
|
||||
// swallow - updatePlugin sets lastError
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ScrollView
|
||||
style={{ flex: 1 }}
|
||||
contentContainerStyle={{ paddingBottom: 38 }}
|
||||
>
|
||||
<Stack
|
||||
style={{ paddingVertical: 24, paddingHorizontal: 12 }}
|
||||
spacing={24}
|
||||
>
|
||||
<TableRowGroup title="Updater">
|
||||
<TableSwitchRow
|
||||
label="Auto Update"
|
||||
subLabel="Automatically install updates when available"
|
||||
icon={<TableRow.Icon source={findAssetId("CloudUploadIcon")!} />}
|
||||
value={autoEnabled}
|
||||
onValueChange={setAutoUpdate}
|
||||
/>
|
||||
|
||||
<TableSwitchRow
|
||||
label="Fetch external plugins on startup"
|
||||
subLabel="Reduce startup network activity when disabled"
|
||||
icon={<TableRow.Icon source={findAssetId("CloudDownloadIcon")!} />}
|
||||
value={fetchPluginsOnStart}
|
||||
onValueChange={setFetchOnStart}
|
||||
/>
|
||||
|
||||
<TableRow
|
||||
label="Last checked"
|
||||
subLabel={
|
||||
lastChecked ? new Date(lastChecked).toLocaleString() : "Never"
|
||||
}
|
||||
icon={<TableRow.Icon source={findAssetId("ClockIcon")!} />}
|
||||
/>
|
||||
|
||||
<TableRow
|
||||
label="Bundle"
|
||||
subLabel={`Version ${debugInfo.bunny.version} — Loader ${debugInfo.bunny.loader.version}`}
|
||||
icon={
|
||||
<TableRow.Icon
|
||||
source={findAssetId("CircleInformationIcon-primary")!}
|
||||
/>
|
||||
}
|
||||
trailing={
|
||||
<IconButton icon={findAssetId("RetryIcon")!} onPress={refresh} />
|
||||
}
|
||||
/>
|
||||
</TableRowGroup>
|
||||
|
||||
<TableRowGroup title="Available Plugin Updates">
|
||||
{lastError ? (
|
||||
<Text variant="text-sm/medium" color="text-danger">
|
||||
Error: {lastError}
|
||||
</Text>
|
||||
) : null}
|
||||
|
||||
{checking ? (
|
||||
<Text variant="text-sm/medium" color="text-muted">
|
||||
Checking for updates...
|
||||
</Text>
|
||||
) : available.length === 0 ? (
|
||||
<Text variant="text-sm/medium" color="text-muted">
|
||||
No updates available. Press the refresh icon in Bundle to check.
|
||||
</Text>
|
||||
) : (
|
||||
available.map((p) => (
|
||||
<Card key={p.id} style={{ padding: 10, marginBottom: 10 }}>
|
||||
<View
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<View style={{ flex: 1 }}>
|
||||
<Text style={{ fontWeight: "bold" }}>{p.name}</Text>
|
||||
<Text>
|
||||
Current: {p.current} — Latest: {p.latest}
|
||||
</Text>
|
||||
<Text style={{ color: "#666", marginTop: 6, fontSize: 12 }}>
|
||||
{p.repo}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={{ marginLeft: 10 }}>
|
||||
<Button
|
||||
text="Update"
|
||||
variant="primary"
|
||||
onPress={() => updatePlugin(p.id, p.repo)}
|
||||
/>
|
||||
<Button
|
||||
text="Skip"
|
||||
variant="secondary"
|
||||
style={{ marginTop: 8 }}
|
||||
onPress={() =>
|
||||
setAvailable((prev) =>
|
||||
prev.filter((x) => x.id !== p.id),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</Card>
|
||||
))
|
||||
)}
|
||||
|
||||
{available.length > 0 ? (
|
||||
<View style={{ marginTop: 6 }}>
|
||||
<Button text="Update All" variant="primary" onPress={updateAll} />
|
||||
</View>
|
||||
) : null}
|
||||
</TableRowGroup>
|
||||
</Stack>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
117
src/index.ts
117
src/index.ts
|
|
@ -14,6 +14,8 @@ import { patchJsx } from "@lib/api/react/jsx";
|
|||
import { logger } from "@lib/utils/logger";
|
||||
import { patchSettings } from "@ui/settings";
|
||||
import { InteractionManager } from "react-native";
|
||||
import { updaterSettings } from "@lib/api/settings";
|
||||
import { getDebugInfo } from "@lib/api/debug";
|
||||
|
||||
// Debug toggle helper (temporary runtime fallback). The helper is dynamically
|
||||
// imported when needed (to avoid bundling it permanently) and removed after use.
|
||||
|
|
@ -57,43 +59,50 @@ export default async () => {
|
|||
);
|
||||
|
||||
// Deferred work: run after interactions to avoid blocking initial paint and navigation.
|
||||
const runDeferred = () => {
|
||||
const runDeferred = async () => {
|
||||
// Initialize Vendetta plugins (may start many plugins) — do not block UI.
|
||||
VdPluginManager.initPlugins()
|
||||
.then((u) => lib.unload.push(u))
|
||||
.catch((e) => logger.log("Vendetta init failed:", e));
|
||||
|
||||
// Ensure core plugins and repository metadata are registered/updated BEFORE
|
||||
// attempting to start ShiggyCord (Bunny) plugins. This makes corePluginInstances
|
||||
// available to `initPlugins()` so built-in core plugins are handled correctly.
|
||||
updatePlugins()
|
||||
.catch((e) => logger.log("updatePlugins failed (deferred):", e))
|
||||
.finally(async () => {
|
||||
// Start ShiggyCord (Bunny) plugins (reads storage and may compile/instantiate plugins).
|
||||
initPlugins();
|
||||
// Start ShiggyCord (Bunny) plugins now without forcing repository updates.
|
||||
// Plugin repository fetching is deferred so the app can finish launching first.
|
||||
// Stagger plugin startup to reduce CPU/memory spikes: use smaller batches and a small interval.
|
||||
// This keeps the UI responsive while plugins initialize in the background.
|
||||
initPlugins({ staggerInterval: 500, batchSize: 2 });
|
||||
|
||||
// Attempt a lightweight recovery toggle if some core plugins failed to start.
|
||||
try {
|
||||
// Dynamically import the helper (if present) but suppress verbose errors.
|
||||
const mod = await import("@core/debug/toggleCorePlugins").catch(
|
||||
() => null,
|
||||
);
|
||||
if (mod?.default) {
|
||||
// Run helper with minimal noise; ignore failures.
|
||||
mod.default({ offDuration: 1500 }).catch(() => {});
|
||||
}
|
||||
// Attempt a lightweight recovery toggle if some core plugins failed to start.
|
||||
try {
|
||||
// Dynamically import the helper (if present) but suppress verbose errors.
|
||||
const mod = await import("@core/debug/toggleCorePlugins").catch(
|
||||
() => null,
|
||||
);
|
||||
if (mod?.default) {
|
||||
// Run helper with minimal noise; ignore failures.
|
||||
mod.default({ offDuration: 1500 }).catch(() => {});
|
||||
}
|
||||
|
||||
// Try to remove the helper source file (best-effort, ignore failures).
|
||||
await import("@lib/api/native/fs")
|
||||
.then((fs) => fs.removeFile("src/core/debug/toggleCorePlugins.ts"))
|
||||
.catch(() => {});
|
||||
} catch {
|
||||
// suppressed
|
||||
}
|
||||
// Try to remove the helper source file (best-effort, ignore failures).
|
||||
await import("@lib/api/native/fs")
|
||||
.then((fs) => fs.removeFile("src/core/debug/toggleCorePlugins.ts"))
|
||||
.catch(() => {});
|
||||
} catch {
|
||||
// suppressed
|
||||
}
|
||||
|
||||
// Update fonts in background
|
||||
updateFonts().catch((e) => logger.log("updateFonts failed:", e));
|
||||
});
|
||||
// Update fonts in background
|
||||
updateFonts().catch((e) => logger.log("updateFonts failed:", e));
|
||||
|
||||
// Schedule plugin repository update after a delay (5 minutes) so update work
|
||||
// does not impact initial launch performance.
|
||||
setTimeout(
|
||||
() => {
|
||||
updatePlugins().catch((e) =>
|
||||
logger.log("updatePlugins failed (deferred 5min):", e),
|
||||
);
|
||||
},
|
||||
5 * 60 * 1000,
|
||||
);
|
||||
|
||||
// Note: we intentionally moved the call to `updatePlugins()` above so core
|
||||
// plugins are registered prior to calling `initPlugins()`.
|
||||
|
|
@ -110,17 +119,59 @@ export default async () => {
|
|||
setTimeout(runDeferred, 200);
|
||||
}
|
||||
|
||||
// Additionally schedule a periodic attempt to update plugins ~3 minutes after start.
|
||||
// This ensures plugin repositories get refreshed even if deferred tasks were delayed.
|
||||
// Schedule a delayed plugin repository refresh (5 minutes) if the earlier deferred schedule didn't run.
|
||||
// This ensures we refresh plugin metadata after the app is fully launched but avoid startup contention.
|
||||
setTimeout(
|
||||
() => {
|
||||
updatePlugins().catch((e) =>
|
||||
logger.log("Scheduled updatePlugins (3min) failed:", e),
|
||||
logger.log("Scheduled updatePlugins (5min) failed:", e),
|
||||
);
|
||||
},
|
||||
3 * 60 * 1000,
|
||||
5 * 60 * 1000,
|
||||
);
|
||||
|
||||
// Periodic bundle check: every 3 hours, check GitHub releases (ignore prereleases).
|
||||
// We use the GitHub Releases API to find the latest non-prerelease release and compare its tag.
|
||||
const checkBundle = async () => {
|
||||
try {
|
||||
const debugInfo = getDebugInfo();
|
||||
const releasesRes = await fetch(
|
||||
"https://api.github.com/repos/kmmiio99o/ShiggyCord/releases",
|
||||
{ cache: "no-store" },
|
||||
);
|
||||
if (!releasesRes.ok) return;
|
||||
const releases = await releasesRes.json();
|
||||
if (!Array.isArray(releases) || releases.length === 0) return;
|
||||
|
||||
// Find first non-prerelease, non-draft release
|
||||
const stable = releases.find((r: any) => !r.prerelease && !r.draft);
|
||||
if (!stable) return;
|
||||
|
||||
const latestTag = stable.tag_name ?? stable.name ?? null;
|
||||
const latestUrl = stable.html_url ?? null;
|
||||
const installed = debugInfo?.bunny?.version ?? null;
|
||||
|
||||
updaterSettings.lastBundleChecked = new Date().toISOString();
|
||||
updaterSettings.lastBundleVersion = latestTag;
|
||||
updaterSettings.bundleLatestTag = latestTag;
|
||||
updaterSettings.bundleLatestUrl = latestUrl ?? null;
|
||||
updaterSettings.bundleAvailable =
|
||||
latestTag != null &&
|
||||
installed != null &&
|
||||
String(latestTag).replace(/^v/, "") !==
|
||||
String(installed).replace(/^v/, "");
|
||||
} catch (err) {
|
||||
logger.log("Bundle check failed:", err);
|
||||
}
|
||||
};
|
||||
|
||||
// Run initial bundle check after a short delay (1 minute) to avoid doing network work
|
||||
// during the critical UI startup path, then run every 3 hours.
|
||||
setTimeout(() => {
|
||||
checkBundle().catch(() => {});
|
||||
}, 60 * 1000);
|
||||
setInterval(checkBundle, 3 * 60 * 60 * 1000);
|
||||
|
||||
// Final ready log for basic UI availability.
|
||||
logger.log("ShiggyCord is ready (UI available).");
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
import { safeFetch } from "@lib/utils";
|
||||
import { OFFICIAL_PLUGINS_REPO_URL } from "@lib/utils/constants";
|
||||
import { semver } from "@metro/common";
|
||||
import { updaterSettings } from "@lib/api/settings";
|
||||
|
||||
import { createBunnyPluginApi } from "./api";
|
||||
import * as t from "./types";
|
||||
|
|
@ -528,16 +529,32 @@ export function getBisectBatches(maxPerStep = 50): string[][] {
|
|||
}
|
||||
|
||||
export async function updateAllRepository() {
|
||||
// Always attempt to update the official repository first
|
||||
try {
|
||||
await updateRepository(OFFICIAL_PLUGINS_REPO_URL);
|
||||
} catch (error) {
|
||||
console.error("Failed to update official plugins repository", error);
|
||||
}
|
||||
|
||||
// Determine global fetch-on-start behavior and per-repo overrides
|
||||
const fetchOnStart = !!(updaterSettings.fetchPluginsOnStart ?? true);
|
||||
const overrides = updaterSettings.repoAutoFetchOverrides ?? {};
|
||||
|
||||
const repos = Object.keys(pluginRepositories);
|
||||
|
||||
await Promise.allSettled(
|
||||
Object.keys(pluginRepositories).map(async (repo) => {
|
||||
if (repo !== OFFICIAL_PLUGINS_REPO_URL) {
|
||||
repos.map(async (repo) => {
|
||||
// Skip official (already handled)
|
||||
if (repo === OFFICIAL_PLUGINS_REPO_URL) return;
|
||||
|
||||
// If global fetching is disabled, only process repos explicitly overridden to true
|
||||
const repoOverride = overrides[repo];
|
||||
if (!fetchOnStart && repoOverride !== true) return;
|
||||
|
||||
try {
|
||||
await updateRepository(repo);
|
||||
} catch (e) {
|
||||
console.error(`Failed to update repository ${repo}`, e);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
|
@ -560,7 +577,36 @@ export async function updatePlugins() {
|
|||
corePluginInstances.set(id, instance);
|
||||
}
|
||||
|
||||
await updateAllRepository();
|
||||
// Always ensure official repository metadata is at least attempted to be fetched,
|
||||
// because core plugin registration / system behavior may depend on it.
|
||||
try {
|
||||
await updateRepository(OFFICIAL_PLUGINS_REPO_URL);
|
||||
} catch (error) {
|
||||
console.error("Failed to update official plugins repository", error);
|
||||
}
|
||||
|
||||
// If fetch-on-start is enabled, update all repositories following configured policy.
|
||||
// Otherwise, only update repositories which have per-repo overrides explicitly set to true.
|
||||
const fetchOnStart = !!(updaterSettings.fetchPluginsOnStart ?? true);
|
||||
const overrides = updaterSettings.repoAutoFetchOverrides ?? {};
|
||||
|
||||
if (fetchOnStart) {
|
||||
await updateAllRepository();
|
||||
} else {
|
||||
// Only fetch explicitly opted-in repositories to avoid startup slowdowns.
|
||||
const repos = Object.keys(pluginRepositories).filter(
|
||||
(r) => r !== OFFICIAL_PLUGINS_REPO_URL && overrides[r] === true,
|
||||
);
|
||||
await Promise.allSettled(
|
||||
repos.map(async (repo) => {
|
||||
try {
|
||||
await updateRepository(repo);
|
||||
} catch (e) {
|
||||
console.error(`Failed to update repository ${repo}`, e);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -25,6 +25,56 @@ export interface LoaderConfig {
|
|||
loadReactDevTools: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* New: Updater settings persisted in MMKV.
|
||||
* Fields:
|
||||
* - autoUpdateEnabled: whether updates are applied automatically
|
||||
* - checkOnStart: whether to check for updates on app start
|
||||
* - updateChannel: optional update channel (stable/beta/alpha)
|
||||
* - lastChecked: ISO timestamp of last update check
|
||||
*
|
||||
* Plugin-related additions:
|
||||
* - fetchPluginsOnStart: when true, plugins (external repositories) are fetched at startup;
|
||||
* when false, plugin repository fetching is deferred and controlled from the Updater UI.
|
||||
* - repoAutoFetchOverrides: per-repository overrides for auto-fetch behavior
|
||||
* (keyed by repository URL, boolean value overrides fetchPluginsOnStart).
|
||||
*/
|
||||
export interface UpdaterSettings {
|
||||
// Whether updates (bundle-level auto-updates) should be applied automatically
|
||||
autoUpdateEnabled: boolean;
|
||||
// Whether to perform an update check on app start
|
||||
checkOnStart: boolean;
|
||||
// Optional channel preference for updates
|
||||
updateChannel?: "stable" | "beta" | "alpha";
|
||||
// ISO timestamp of the last time the updater checked (plugins + bundle)
|
||||
lastChecked?: string | null;
|
||||
|
||||
// -------------------------
|
||||
// Plugin-related settings
|
||||
// -------------------------
|
||||
// Whether external plugin repositories should be fetched at startup
|
||||
fetchPluginsOnStart?: boolean;
|
||||
// Per-repo override for auto-fetch behavior (repoUrl => boolean)
|
||||
repoAutoFetchOverrides?: Record<string, boolean>;
|
||||
|
||||
// -------------------------
|
||||
// Bundle update settings
|
||||
// -------------------------
|
||||
// How often (in hours) to check the project's releases for bundle updates.
|
||||
// Default should be treated as 3 if unset.
|
||||
bundleCheckIntervalHours?: number;
|
||||
// Releases URL (used for checking the bundle). Defaults to:
|
||||
// https://github.com/kmmiio99o/ShiggyCord/releases/
|
||||
bundleReleasesUrl?: string;
|
||||
// Whether to ignore prerelease releases when checking bundle updates.
|
||||
// Defaults to true.
|
||||
bundleIgnorePrereleases?: boolean;
|
||||
// ISO timestamp of the last bundle check
|
||||
lastBundleChecked?: string | null;
|
||||
// Last known bundle version discovered by the updater (if any)
|
||||
lastBundleVersion?: string | null;
|
||||
}
|
||||
|
||||
export const settings = wrapSync(
|
||||
createStorage<Settings>(createMMKVBackend("VENDETTA_SETTINGS")),
|
||||
);
|
||||
|
|
@ -36,6 +86,18 @@ export const loaderConfig = wrapSync(
|
|||
enabled: false,
|
||||
url: "http://localhost:4040/shiggycord.js",
|
||||
},
|
||||
loadReactDevTools: false,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Updater settings storage.
|
||||
* Stored under MMKV key: VENDETTA_UPDATER_SETTINGS
|
||||
* Defaults should be handled by UI code when reading values if necessary.
|
||||
*/
|
||||
export const updaterSettings = wrapSync(
|
||||
createStorage<UpdaterSettings>(
|
||||
createMMKVBackend("VENDETTA_UPDATER_SETTINGS"),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue