[SafeMode] Initial implementation (#61)

* [UI > Components] Add Discord's button

* [SafeMode] Initial work, basic ErrorBoundary patch

* [SafeMode] Custom error boundary (#57)

* [SafeMode] Add mostly complete custom error boundary

* [SafeMode] Use Button from @ui/components in error boundary

* [SafeMode] Wrap the error boundary in our own error boundary

* [SafeMode > ErrorBoundary] Code-style changes

---------

Co-authored-by: Beef <beefers@riseup.net>

* [TS] Add basic type for Discord's button

* [UI] Move Codeblock to components, and use it

* [UI > Settings] Allow disabling the ErrorBoundary in CustomPage

* [UI > Settings] Move the ErrorBoundary triggers to Developer

* [TS] Add Codeblock to types

* [TS] Use ButtonColors in Button type

* [SafeMode > ErrorBoundary] Rework

* [UI] Add HelpMessage to components

* [SafeMode] Proper implementation

* [Global] SafeMode is optional (#59)

* [UI > Developer] Restore the balance

* [SafeMode > ErrorBoundary] Optimise for tablet UI

* [SafeMode] Last-minute fixes

---------

Co-authored-by: Jack <30497388+FieryFlames@users.noreply.github.com>
Co-authored-by: Jack Matthews <jm5112356@gmail.com>
This commit is contained in:
Beef 2023-04-13 18:42:14 +00:00 committed by GitHub
parent f85fc4b00c
commit 5344f0017a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 293 additions and 47 deletions

View file

@ -1,12 +1,27 @@
import { RNConstants } from "@types";
import { ReactNative as RN } from "@metro/common";
import { after } from "@lib/patcher";
import { ClientInfoManager, DeviceManager } from "@lib/native";
import { ClientInfoManager, DeviceManager, BundleUpdaterManager } from "@lib/native";
import { getCurrentTheme, selectTheme } from "@lib/themes";
import { getAssetIDByName } from "@ui/assets";
import { showToast } from "@ui/toasts";
import settings from "@lib/settings";
import logger from "@lib/logger";
export let socket: WebSocket;
export async function toggleSafeMode() {
settings.safeMode = { ...settings.safeMode, enabled: !settings.safeMode?.enabled }
if (window.__vendetta_loader?.features.themes) {
if (getCurrentTheme()?.id) settings.safeMode!.currentThemeId = getCurrentTheme()!.id;
if (settings.safeMode?.enabled) {
await selectTheme("default");
} else if (settings.safeMode?.currentThemeId) {
await selectTheme(settings.safeMode?.currentThemeId);
}
}
setTimeout(BundleUpdaterManager.reload);
}
export function connectToDebugger(url: string) {
if (socket !== undefined && socket.readyState !== WebSocket.CLOSED) socket.close();
@ -16,7 +31,7 @@ export function connectToDebugger(url: string) {
}
socket = new WebSocket(`ws://${url}`);
socket.addEventListener("open", () => showToast("Connected to debugger.", getAssetIDByName("Check")));
socket.addEventListener("message", (message: any) => {
try {
@ -32,7 +47,7 @@ export function connectToDebugger(url: string) {
});
}
export function patchLogHook() {
export function patchLogHook() {
const unpatch = after("nativeLoggingHook", globalThis, (args) => {
if (socket?.readyState === WebSocket.OPEN) socket.send(JSON.stringify({ message: args[0], level: args[1] }));
logger.log(args[0]);

View file

@ -1,6 +1,7 @@
import { Indexable, PluginManifest, Plugin } from "@types";
import { awaitSyncWrapper, createMMKVBackend, createStorage, wrapSync } from "@lib/storage";
import { MMKVManager } from "@lib/native";
import settings from "@lib/settings";
import logger, { logModule } from "@lib/logger";
import safeFetch from "@utils/safeFetch";
@ -76,9 +77,11 @@ export async function startPlugin(id: string) {
if (!plugin) throw new Error("Attempted to start non-existent plugin");
try {
const pluginRet: EvaledPlugin = await evalPlugin(plugin);
loadedPlugins[id] = pluginRet;
pluginRet.onLoad?.();
if (!settings.safeMode?.enabled) {
const pluginRet: EvaledPlugin = await evalPlugin(plugin);
loadedPlugins[id] = pluginRet;
pluginRet.onLoad?.();
}
plugin.enabled = true;
} catch(e) {
logger.error(`Plugin ${plugin.id} errored whilst loading, and will be unloaded`, e);
@ -99,15 +102,17 @@ export function stopPlugin(id: string, disable = true) {
const plugin = plugins[id];
const pluginRet = loadedPlugins[id];
if (!plugin) throw new Error("Attempted to stop non-existent plugin");
if (!pluginRet) throw new Error("Attempted to stop a non-started plugin");
try {
pluginRet.onUnload?.();
} catch(e) {
logger.error(`Plugin ${plugin.id} errored whilst unloading`, e);
if (!settings.safeMode?.enabled) {
try {
pluginRet?.onUnload?.();
} catch(e) {
logger.error(`Plugin ${plugin.id} errored whilst unloading`, e);
}
delete loadedPlugins[id];
}
delete loadedPlugins[id];
disable && (plugin.enabled = false);
}
@ -120,13 +125,16 @@ export function removePlugin(id: string) {
}
export async function initPlugins() {
await awaitSyncWrapper(settings);
await awaitSyncWrapper(plugins);
const allIds = Object.keys(plugins);
// Loop over any plugin that is enabled, update it if allowed, then start it.
await Promise.allSettled(allIds.filter(pl => plugins[pl].enabled).map(async (pl) => (plugins[pl].update && await fetchPlugin(pl), await startPlugin(pl))));
// Wait for the above to finish, then update all disabled plugins that are allowed to.
allIds.filter(pl => !plugins[pl].enabled && plugins[pl].update).forEach(pl => fetchPlugin(pl));
if (!settings.safeMode?.enabled) {
// Loop over any plugin that is enabled, update it if allowed, then start it.
await Promise.allSettled(allIds.filter(pl => plugins[pl].enabled).map(async (pl) => (plugins[pl].update && await fetchPlugin(pl), await startPlugin(pl))));
// Wait for the above to finish, then update all disabled plugins that are allowed to.
allIds.filter(pl => !plugins[pl].enabled && plugins[pl].update).forEach(pl => fetchPlugin(pl));
};
return stopAllPlugins;
}

View file

@ -22,7 +22,7 @@ export default async (unloads: any[]): Promise<VendettaObject> => ({
metro: { ...metro, common: { ...common } },
constants,
utils,
debug: utils.without(debug, "versionHash", "patchLogHook"),
debug: utils.without(debug, "versionHash", "patchLogHook", "toggleSafeMode"),
ui: {
components,
toasts,