Revert "feat: detach Sidebar patches as they"

This reverts commit ecd97c7018.
This commit is contained in:
kmmiio99o 2026-05-24 16:05:48 +02:00
parent 6ca9423849
commit a2a20ab1a1
No known key found for this signature in database
GPG key ID: AC9D7747D423B9BF
11 changed files with 827 additions and 2 deletions

178
chatboxavatar/index.tsx Normal file
View file

@ -0,0 +1,178 @@
import { after } from "@api/patcher";
import { findByNameLazy, findByProps, findByPropsLazy, findByStoreName, findByTypeDisplayName } from "@metro";
import { ReactNative } from "@metro/common";
import { definePlugin } from "@plugins";
import { Contributors } from "@rain/Developers";
import React, { useEffect, useRef } from "react";
import ChatboxAvatarSettings from "./settings";
import { useChatboxAvatarSettings } from "./storage";
const Flux = findByProps("useStateFromStores");
const ChatInputActions = findByTypeDisplayName("ChatInputActions");
const ChatInputSendButton = findByTypeDisplayName("ChatInputSendButton");
let hasText = false;
let sendBtnRef: { setHasText?: (hasText: boolean) => void } | undefined;
const { Pressable, View, Animated } = ReactNative;
const avatarCollapse = new Animated.Value(0);
const Avatar = findByPropsLazy("default", "AvatarSizes", "getStatusSize")?.default;
const UserStore = findByStoreName("UserStore");
const SelectedChannelStore = findByStoreName("SelectedChannelStore");
const ChannelStore = findByStoreName("ChannelStore");
const SelfPresenceStore = findByStoreName("SelfPresenceStore");
const showUserProfileActionSheet = findByNameLazy("showUserProfileActionSheet");
const showYouAccountActionSheetByProp = findByPropsLazy("showYouAccountActionSheet");
function AvatarAction() {
const [textState, setTextState] = React.useState(false);
const self = Flux?.useStateFromStores?.([UserStore], () => UserStore?.getCurrentUser?.());
const status = Flux?.useStateFromStores?.([SelfPresenceStore], () => SelfPresenceStore?.getStatus?.());
const settings = useChatboxAvatarSettings();
const channelId = Flux?.useStateFromStores?.([SelectedChannelStore], () => SelectedChannelStore?.getCurrentlySelectedChannelId?.());
const channel = Flux?.useStateFromStores?.([ChannelStore], () => ChannelStore?.getChannel?.(channelId), [channelId]);
const animated = useRef(avatarCollapse).current;
React.useEffect(() => {
const interval = setInterval(() => {
setTextState(hasText);
}, 100);
return () => clearInterval(interval);
}, []);
useEffect(() => {
const shouldCollapse = settings.collapseWhileTyping && textState;
Animated.parallel([
Animated.timing(animated, {
toValue: shouldCollapse ? 1 : 0,
duration: 200,
useNativeDriver: false,
}),
]).start();
}, [textState, settings.collapseWhileTyping, animated]);
if (!self) return null;
const openAccountSheet = () => {
const fn = showYouAccountActionSheetByProp?.showYouAccountActionSheet;
if (typeof fn === "function") {
try {
fn(true, true);
return;
} catch (err) {
}
}
showUserProfileActionSheet?.({ userId: self.id, channelId: channel?.id ?? channelId });
};
const handlePress = () => {
switch (settings.pressAction) {
case "profile":
showUserProfileActionSheet?.({ userId: self.id, channelId: channel?.id ?? channelId });
break;
case "server":
openAccountSheet();
break;
default:
break;
}
};
const handleLongPress = () => {
switch (settings.longPressAction) {
case "profile":
showUserProfileActionSheet?.({ userId: self.id, channelId: channel?.id ?? channelId });
break;
case "server":
openAccountSheet();
break;
default:
break;
}
};
return (
<Animated.View
style={{
height: 40,
width: animated.interpolate({ inputRange: [0, 1], outputRange: [40, 0] }),
marginHorizontal: animated.interpolate({ inputRange: [0, 1], outputRange: [4, 0] }),
flexShrink: 0,
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
overflow: settings.collapseWhileTyping ? "hidden" : "visible",
}}
>
<Pressable
onPress={handlePress}
onLongPress={handleLongPress}
>
{Avatar && (
<Avatar
user={self}
guildId={channel?.guild_id}
status={settings.showStatusCutout ? status : undefined}
avatarDecoration={self?.avatarDecoration}
animate={!settings.collapseWhileTyping || !textState}
/>
)}
</Pressable>
</Animated.View>
);
}
const unpatches: (() => void)[] = [];
export default definePlugin({
name: "ChatboxAvatar",
description: "Adds your avatar to the chatbox.",
author: [Contributors.LampDelivery],
id: "chatboxavatar",
version: "v1.0.0",
requiresRestart: true,
settings: ChatboxAvatarSettings,
async start() {
if (!ChatInputActions?.type || !ChatInputSendButton?.type) return;
unpatches.push(
after("render", ChatInputSendButton.type, (args, ret) => {
setImmediate(() => {
setImmediate(() => {
if (args?.[1]?.current) {
sendBtnRef = args[1].current;
const origSetHasText = sendBtnRef?.setHasText;
unpatches.push(() => { if (sendBtnRef && origSetHasText) sendBtnRef.setHasText = origSetHasText; });
if (sendBtnRef) {
sendBtnRef.setHasText = (hasText_: boolean) => {
hasText = hasText_;
if (origSetHasText) return origSetHasText.call(sendBtnRef, hasText_);
};
}
}
});
});
})
);
},
async eagerStart() {
if (!ChatInputActions?.type || !ChatInputSendButton?.type) return;
unpatches.push(
after("render", ChatInputActions.type, (args, ret) => {
return React.createElement(
View,
{ style: { flexDirection: "row", alignItems: "center" } },
ret,
React.createElement(AvatarAction)
);
})
);
},
stop() {
for (const unpatch of unpatches) unpatch?.();
unpatches.length = 0;
},
});

View file

@ -0,0 +1,58 @@
import { Stack, TableRadioGroup, TableRadioRow } from "@metro/common/components";
import React from "react";
import { ChatboxAvatarSettings, useChatboxAvatarSettings } from "./storage";
export default function ChatboxAvatarSettings() {
const settings = useChatboxAvatarSettings();
const [, forceUpdate] = React.useReducer(x => ~x, 0);
const setPressAction = (value: string) => {
useChatboxAvatarSettings.getState().updateSettings?.({ pressAction: value as ChatboxAvatarSettings["pressAction"] });
forceUpdate();
};
const setLongPressAction = (value: string) => {
useChatboxAvatarSettings.getState().updateSettings?.({ longPressAction: value as ChatboxAvatarSettings["longPressAction"] });
forceUpdate();
};
return (
<Stack style={{ padding: 16 }} spacing={16}>
<TableRadioGroup
value={settings.pressAction}
onChange={setPressAction}
title="Avatar Press Action"
>
<TableRadioRow value="profile" label="Open Profile" />
<TableRadioRow value="server" label="Open Status Picker" />
</TableRadioGroup>
<TableRadioGroup
value={settings.longPressAction}
onChange={setLongPressAction}
title="Avatar Long-Press Action"
>
<TableRadioRow value="profile" label="Open Profile" />
<TableRadioRow value="server" label="Open Status Picker" />
</TableRadioGroup>
<TableRadioGroup
value={settings.showStatusCutout ? "true" : "false"}
onChange={v => {
useChatboxAvatarSettings.getState().updateSettings?.({ showStatusCutout: v === "true" });
forceUpdate();
}}
title="Status Icon"
>
<TableRadioRow value="true" label="Show" />
<TableRadioRow value="false" label="Hide" />
</TableRadioGroup>
<TableRadioGroup
value={settings.collapseWhileTyping ? "true" : "false"}
onChange={v => {
useChatboxAvatarSettings.getState().updateSettings?.({ collapseWhileTyping: v === "true" });
forceUpdate();
}}
title="Collapse While Typing"
>
<TableRadioRow value="true" label="Collapse avatar while typing" />
<TableRadioRow value="false" label="Always show avatar" />
</TableRadioGroup>
</Stack>
);
}

22
chatboxavatar/storage.ts Normal file
View file

@ -0,0 +1,22 @@
import { createPluginStore } from "@api/storage";
export interface ChatboxAvatarSettings {
replaceButton: "gallery" | "apps" | "gift";
pressAction: "profile" | "server";
longPressAction: "profile" | "server";
serverId?: string;
showStatusCutout?: boolean;
collapseWhileTyping?: boolean;
}
export const {
useStore: useChatboxAvatarSettings,
settings: chatboxAvatarSettings,
} = createPluginStore<ChatboxAvatarSettings>("chatboxavatar", {
replaceButton: "gallery",
pressAction: "profile",
longPressAction: "server",
serverId: "",
showStatusCutout: false,
collapseWhileTyping: false,
});

85
eslint.config.mjs Normal file
View file

@ -0,0 +1,85 @@
import eslint from "@eslint/js";
import tseslintPlugin from "@typescript-eslint/eslint-plugin";
import tseslintParser from "@typescript-eslint/parser";
import reactPlugin from "eslint-plugin-react";
import globals from "globals";
export default [
eslint.configs.recommended,
{
files: ["**/*.{ts,tsx}"],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
parser: tseslintParser,
globals: {
...globals.node,
...globals.browser,
NodeJS: "readonly",
},
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},
plugins: {
"@typescript-eslint": tseslintPlugin,
react: reactPlugin,
},
settings: {
react: {
version: "detect",
},
},
rules: {
...tseslintPlugin.configs.recommended.rules,
...reactPlugin.configs.recommended.rules,
indent: ["error", 4, { SwitchCase: 1 }],
quotes: ["error", "double"],
semi: ["error", "always"],
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/triple-slash-reference": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"react/react-in-jsx-scope": "off",
"eol-last": "error",
},
},
{
files: ["**/*.{js,jsx}"],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: {
...globals.node,
...globals.browser,
NodeJS: "readonly",
},
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},
plugins: {
react: reactPlugin,
},
settings: {
react: {
version: "detect",
},
},
rules: {
...reactPlugin.configs.recommended.rules,
indent: ["error", 4, { SwitchCase: 1 }],
quotes: ["error", "double"],
semi: ["error", "always"],
"react/react-in-jsx-scope": "off",
"eol-last": "error",
},
},
{
ignores: ["dist/**", "node_modules/**"],
},
];

View file

@ -0,0 +1,18 @@
{
"name": "Chatbox Avatar",
"description": "Quickly view your profile from the chatbox.",
"authors": [
{
"name": "Cynosphere",
"id": "150745989836308480"
},
{
"name": "kmmiio99o",
"id": "879393496627306587"
}
],
"main": "src/index.tsx",
"vendetta": {
"icon": "UserSquareIcon"
}
}

View file

@ -0,0 +1,165 @@
import { after } from "@vendetta/patcher";
import { findByName, findByProps, findByStoreName, findByDisplayName } from "@vendetta/metro";
import { ReactNative } from "@vendetta/metro/common";
import { plugin } from "@vendetta";
import React, { useEffect, useRef } from "react";
import ChatboxAvatarSettings from "./settings";
const Flux = findByProps("useStateFromStores");
const ChatInputActions = findByDisplayName("ChatInputActions");
const ChatInputSendButton = findByDisplayName("ChatInputSendButton");
let hasText = false;
let sendBtnRef: { setHasText?: (hasText: boolean) => void } | undefined;
const { Pressable, View, Animated } = ReactNative;
const avatarCollapse = new Animated.Value(0);
const Avatar = findByProps("default", "AvatarSizes", "getStatusSize")?.default;
const UserStore = findByStoreName("UserStore");
const SelectedChannelStore = findByStoreName("SelectedChannelStore");
const ChannelStore = findByStoreName("ChannelStore");
const SelfPresenceStore = findByStoreName("SelfPresenceStore");
const showUserProfileActionSheet = findByName("showUserProfileActionSheet");
const showYouAccountActionSheetByProp = findByProps("showYouAccountActionSheet");
function AvatarAction() {
const [textState, setTextState] = React.useState(false);
const self = Flux?.useStateFromStores?.([UserStore], () => UserStore?.getCurrentUser?.());
const status = Flux?.useStateFromStores?.([SelfPresenceStore], () => SelfPresenceStore?.getStatus?.());
const settings = plugin.storage;
const channelId = Flux?.useStateFromStores?.([SelectedChannelStore], () => SelectedChannelStore?.getCurrentlySelectedChannelId?.());
const channel = Flux?.useStateFromStores?.([ChannelStore], () => ChannelStore?.getChannel?.(channelId), [channelId]);
const animated = useRef(avatarCollapse).current;
React.useEffect(() => {
const interval = setInterval(() => {
setTextState(hasText);
}, 100);
return () => clearInterval(interval);
}, []);
useEffect(() => {
const shouldCollapse = settings.collapseWhileTyping && textState;
Animated.parallel([
Animated.timing(animated, {
toValue: shouldCollapse ? 1 : 0,
duration: 200,
useNativeDriver: false,
}),
]).start();
}, [textState, settings.collapseWhileTyping, animated]);
if (!self) return null;
const openAccountSheet = () => {
const fn = showYouAccountActionSheetByProp?.showYouAccountActionSheet;
if (typeof fn === "function") {
try {
fn(true, true);
return;
} catch {}
}
showUserProfileActionSheet?.({ userId: self.id, channelId: channel?.id ?? channelId });
};
const handlePress = () => {
switch (settings.pressAction) {
case "profile":
showUserProfileActionSheet?.({ userId: self.id, channelId: channel?.id ?? channelId });
break;
case "server":
openAccountSheet();
break;
default:
break;
}
};
const handleLongPress = () => {
switch (settings.longPressAction) {
case "profile":
showUserProfileActionSheet?.({ userId: self.id, channelId: channel?.id ?? channelId });
break;
case "server":
openAccountSheet();
break;
default:
break;
}
};
return (
<Animated.View
style={{
height: 40,
width: animated.interpolate({ inputRange: [0, 1], outputRange: [40, 0] }),
marginHorizontal: animated.interpolate({ inputRange: [0, 1], outputRange: [4, 0] }),
flexShrink: 0,
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
overflow: settings.collapseWhileTyping ? "hidden" : "visible",
}}
>
<Pressable
onPress={handlePress}
onLongPress={handleLongPress}
>
{Avatar && (
<Avatar
user={self}
guildId={channel?.guild_id}
status={settings.showStatusCutout ? status : undefined}
avatarDecoration={self?.avatarDecoration}
animate={!settings.collapseWhileTyping || !textState}
/>
)}
</Pressable>
</Animated.View>
);
}
const unpatches: (() => void)[] = [];
export default {
onLoad() {
if (!ChatInputActions?.prototype?.render || !ChatInputSendButton?.prototype?.render) return;
unpatches.push(
after("render", ChatInputActions.prototype, (args, ret) => {
return React.createElement(
View,
{ style: { flexDirection: "row", alignItems: "center" } },
ret,
React.createElement(AvatarAction)
);
})
);
unpatches.push(
after("render", ChatInputSendButton.prototype, (args, ret) => {
setImmediate(() => {
setImmediate(() => {
if (args?.[1]?.current) {
sendBtnRef = args[1].current;
const origSetHasText = sendBtnRef?.setHasText;
unpatches.push(() => { if (sendBtnRef && origSetHasText) sendBtnRef.setHasText = origSetHasText; });
if (sendBtnRef) {
sendBtnRef.setHasText = (hasText_: boolean) => {
hasText = hasText_;
if (origSetHasText) return origSetHasText.call(sendBtnRef, hasText_);
};
}
}
});
});
})
);
},
onUnload() {
for (const unpatch of unpatches) unpatch?.();
unpatches.length = 0;
},
settings: ChatboxAvatarSettings,
};

View file

@ -0,0 +1,101 @@
import { plugin } from "@vendetta";
import { useProxy } from "@vendetta/storage";
import { React, ReactNative } from "@vendetta/metro/common";
import { findByProps } from "@vendetta/metro";
const { ScrollView } = ReactNative;
const { TableRowGroup, TableCheckboxRow, Stack } = findByProps(
"TableCheckboxRow", "TableRowGroup", "Stack"
);
plugin.storage.pressAction ??= "profile";
plugin.storage.longPressAction ??= "server";
plugin.storage.showStatusCutout ??= false;
plugin.storage.collapseWhileTyping ??= false;
export default function ChatboxAvatarSettings() {
useProxy(plugin.storage);
const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
return (
<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 10 }}>
<Stack spacing={8}>
<TableRowGroup title="Avatar Press Action">
<TableCheckboxRow
label="Open Profile"
checked={plugin.storage.pressAction === "profile"}
onPress={() => {
plugin.storage.pressAction = "profile";
forceUpdate();
}}
/>
<TableCheckboxRow
label="Open Status Picker"
checked={plugin.storage.pressAction === "server"}
onPress={() => {
plugin.storage.pressAction = "server";
forceUpdate();
}}
/>
</TableRowGroup>
<TableRowGroup title="Avatar Long-Press Action">
<TableCheckboxRow
label="Open Profile"
checked={plugin.storage.longPressAction === "profile"}
onPress={() => {
plugin.storage.longPressAction = "profile";
forceUpdate();
}}
/>
<TableCheckboxRow
label="Open Status Picker"
checked={plugin.storage.longPressAction === "server"}
onPress={() => {
plugin.storage.longPressAction = "server";
forceUpdate();
}}
/>
</TableRowGroup>
<TableRowGroup title="Status Icon">
<TableCheckboxRow
label="Show"
checked={!!plugin.storage.showStatusCutout}
onPress={() => {
plugin.storage.showStatusCutout = true;
forceUpdate();
}}
/>
<TableCheckboxRow
label="Hide"
checked={!plugin.storage.showStatusCutout}
onPress={() => {
plugin.storage.showStatusCutout = false;
forceUpdate();
}}
/>
</TableRowGroup>
<TableRowGroup title="Collapse While Typing">
<TableCheckboxRow
label="Collapse avatar while typing"
checked={!!plugin.storage.collapseWhileTyping}
onPress={() => {
plugin.storage.collapseWhileTyping = true;
forceUpdate();
}}
/>
<TableCheckboxRow
label="Always show avatar"
checked={!plugin.storage.collapseWhileTyping}
onPress={() => {
plugin.storage.collapseWhileTyping = false;
forceUpdate();
}}
/>
</TableRowGroup>
</Stack>
</ScrollView>
);
}

View file

@ -26,6 +26,7 @@ import {
friendInviteRevokeCommand, friendInviteRevokeCommand,
} from "./src/commands/friendinvites"; } from "./src/commands/friendinvites";
import settings from "./settings/settings"; import settings from "./settings/settings";
import patchSidebar from "./sidebar";
if (!storage.factSettings) { if (!storage.factSettings) {
storage.factSettings = { storage.factSettings = {
@ -76,6 +77,10 @@ if (!storage.hiddenSettings) {
konochanBypassNsfw: false, konochanBypassNsfw: false,
}; };
} }
// sidebar enabled setting
if (storage.sidebarEnabled === undefined) {
storage.sidebarEnabled = true;
}
const commandMap = { const commandMap = {
catfact: catFactCommand, catfact: catFactCommand,
@ -102,11 +107,13 @@ const commandMap = {
}; };
let commands: Array<() => void> = []; let commands: Array<() => void> = [];
let sidebarUnpatch: (() => void) | undefined;
export default { export default {
onLoad: () => { onLoad: () => {
console.log("[Commands Plugin] Loading..."); console.log("[Commands Plugin] Loading...");
<<<<<<< HEAD
// Register commands // Register commands
for (const [key, command] of Object.entries(commandMap)) { for (const [key, command] of Object.entries(commandMap)) {
if (storage.enabledCommands[key]) { if (storage.enabledCommands[key]) {
@ -120,11 +127,35 @@ export default {
); );
} }
} }
=======
// Patch sidebar if enabled
if (storage.sidebarEnabled !== false) {
try {
sidebarUnpatch = patchSidebar();
console.log("[Commands Plugin] Sidebar patched successfully");
} catch (error) {
console.error("[Commands Plugin] Failed to patch sidebar:", error);
}
}
// Register commands
for (const [key, command] of Object.entries(commandMap)) {
if (storage.enabledCommands[key]) {
try {
commands.push(registerCommand(command as any));
console.log(`[Commands Plugin] Registered command: ${key}`);
} catch (error) {
console.error(
`[Commands Plugin] Failed to register command ${key}:`,
error,
);
>>>>>>> parent of ecd97c7 (feat: detach Sidebar patches as they)
} }
}, },
onUnload: () => { onUnload: () => {
console.log("[Commands Plugin] Unloading..."); console.log("[Commands Plugin] Unloading...");
<<<<<<< HEAD
// Unregister commands // Unregister commands
commands.forEach((unregister) => { commands.forEach((unregister) => {
try { try {
@ -136,4 +167,28 @@ export default {
commands = []; commands = [];
}, },
settings, settings,
=======
// Unregister commands
commands.forEach((unregister) => {
try {
unregister();
} catch (error) {
// Ignore errors during cleanup
}
});
commands = [];
// Unpatch sidebar
if (sidebarUnpatch) {
try {
sidebarUnpatch();
sidebarUnpatch = undefined;
console.log("[Commands Plugin] Sidebar unpatched");
} catch (error) {
console.error("[Commands Plugin] Failed to unpatch sidebar:", error);
}
}
},
settings,
>>>>>>> parent of ecd97c7 (feat: detach Sidebar patches as they)
}; };

View file

@ -71,8 +71,8 @@ storage.hiddenSettings ??= {
konochanBypassNsfw: false, konochanBypassNsfw: false,
}; };
// Sidebar setting // Sidebar setting (enabled by default)
storage.sidebarEnabled ??= false; storage.sidebarEnabled ??= true;
// MAIN SETTINGS COMPONENT // MAIN SETTINGS COMPONENT
export default function Settings() { export default function Settings() {

View file

@ -8,6 +8,7 @@ import { UserStore } from "./modules";
import { serviceFactory } from "./services/ServiceFactory"; import { serviceFactory } from "./services/ServiceFactory";
import Settings from "./ui/pages/Settings"; import Settings from "./ui/pages/Settings";
import patchSidebar from "./sidebar";
export const pluginState = { export const pluginState = {
pluginStopped: false, pluginStopped: false,
@ -21,6 +22,8 @@ export const pluginState = {
lastTrackUrl?: string; lastTrackUrl?: string;
}; };
let sidebarUnpatch: (() => void) | undefined;
// Initialize default settings // Initialize default settings
const defaultSettings: LFMSettings = Constants.DEFAULT_SETTINGS; const defaultSettings: LFMSettings = Constants.DEFAULT_SETTINGS;
Object.keys(defaultSettings).forEach((key) => { Object.keys(defaultSettings).forEach((key) => {
@ -28,6 +31,8 @@ Object.keys(defaultSettings).forEach((key) => {
plugin.storage[key] ?? defaultSettings[key as keyof typeof defaultSettings]; plugin.storage[key] ?? defaultSettings[key as keyof typeof defaultSettings];
}); });
plugin.storage.addToSidebar ??= false;
export const currentSettings = new Proxy(plugin.storage, { export const currentSettings = new Proxy(plugin.storage, {
get(target, prop: string) { get(target, prop: string) {
return target[prop]; return target[prop];
@ -119,22 +124,97 @@ export default {
console.log("[Multi-Scrobbler] Loading..."); console.log("[Multi-Scrobbler] Loading...");
pluginState.pluginStopped = false; pluginState.pluginStopped = false;
<<<<<<< HEAD
validateAndInitialize(); validateAndInitialize();
}, },
=======
// Patch sidebar if enabled
if (currentSettings.addToSidebar !== false) {
try {
sidebarUnpatch = patchSidebar();
console.log("[Multi-Scrobbler] Sidebar patched successfully");
} catch (error) {
console.error("[Multi-Scrobbler] Failed to patch sidebar:", error);
}
}
validateAndInitialize();
},
>>>>>>> parent of ecd97c7 (feat: detach Sidebar patches as they)
onUnload() { onUnload() {
console.log("[Multi-Scrobbler] Unloading..."); console.log("[Multi-Scrobbler] Unloading...");
pluginState.pluginStopped = true; pluginState.pluginStopped = true;
<<<<<<< HEAD
=======
// Unpatch sidebar
if (sidebarUnpatch) {
try {
sidebarUnpatch();
sidebarUnpatch = undefined;
console.log("[Multi-Scrobbler] Sidebar unpatched");
} catch (error) {
console.error("[Multi-Scrobbler] Failed to unpatch sidebar:", error);
}
}
stop(); stop();
}, },
async onSettingsUpdate(newSettings: any) { async onSettingsUpdate(newSettings: any) {
const oldService = currentSettings.service; const oldService = currentSettings.service;
const newService = newSettings.service; const newService = newSettings.service;
const oldSidebar = currentSettings.addToSidebar;
const newSidebar = newSettings.addToSidebar;
Object.assign(currentSettings, newSettings); Object.assign(currentSettings, newSettings);
// Check if sidebar setting changed
if (oldSidebar !== newSidebar) {
if (newSidebar) {
try {
sidebarUnpatch = patchSidebar();
console.log("[Multi-Scrobbler] Sidebar enabled");
} catch (error) {
console.error("[Multi-Scrobbler] Failed to enable sidebar:", error);
}
} else {
if (sidebarUnpatch) {
try {
sidebarUnpatch();
} catch (e) {
console.error("[Multi-Scrobbler] Failed to unpatch sidebar:", e);
}
sidebarUnpatch = undefined;
console.log("[Multi-Scrobbler] Sidebar disabled");
}
}
}
if (oldService !== newService && newService) {
console.log(`[Multi-Scrobbler] Service changed from ${oldService || "none"} to ${newService}`);
try {
await switchService(newService);
} catch (e) {
console.error("[Multi-Scrobbler] Failed to switch service:", e);
}
} else if (!pluginState.pluginStopped && currentSettings.service) {
tryInitialize();
} else if (!currentSettings.service) {
console.log("[Multi-Scrobbler] Service unselected, stopping plugin...");
try {
>>>>>>> parent of ecd97c7 (feat: detach Sidebar patches as they)
stop();
},
async onSettingsUpdate(newSettings: any) {
const oldService = currentSettings.service;
const newService = newSettings.service;
<<<<<<< HEAD
Object.assign(currentSettings, newSettings);
if (oldService !== newService && newService) { if (oldService !== newService && newService) {
console.log(`[Multi-Scrobbler] Service changed from ${oldService || "none"} to ${newService}`); console.log(`[Multi-Scrobbler] Service changed from ${oldService || "none"} to ${newService}`);
try { try {
@ -163,3 +243,7 @@ export default {
settings: Settings, settings: Settings,
}; };
=======
settings: Settings,
};
>>>>>>> parent of ecd97c7 (feat: detach Sidebar patches as they)

View file

@ -229,6 +229,7 @@ export default function Settings() {
/> />
</TableRowGroup> </TableRowGroup>
<<<<<<< HEAD
{/* Plugin Configuration */} {/* Plugin Configuration */}
<TableRowGroup title="Plugin Configuration"> <TableRowGroup title="Plugin Configuration">
<TableRow <TableRow
@ -286,6 +287,64 @@ export default function Settings() {
disabled={true} disabled={true}
/> />
</TableRowGroup> </TableRowGroup>
=======
{/* Plugin Configuration */}
<TableRowGroup title="Plugin Configuration">
<TableRow
label="Display Settings"
subLabel="Customize app name and update interval"
trailing={<TableRow.Arrow />}
onPress={() =>
navigation.push("VendettaCustomPage", {
title: "Display Settings",
render: DisplaySettingsPage,
})
}
/>
<TableRow
label="RPC Customization"
subLabel="Customize Discord rich presence display options"
trailing={<TableRow.Arrow />}
onPress={() =>
navigation.push("VendettaCustomPage", {
title: "RPC Customization",
render: RPCCustomizationSettingsPage,
})
}
/>
<TableRow
label="Ignore List"
subLabel="Configure apps that should hide your status"
trailing={<TableRow.Arrow />}
onPress={() =>
navigation.push("VendettaCustomPage", {
title: "Ignore List Settings",
render: IgnoreListSettingsPage,
})
}
/>
<TableRow
label="Logging Settings"
subLabel="Configure logging and debugging options"
trailing={<TableRow.Arrow />}
onPress={() =>
navigation.push("VendettaCustomPage", {
title: "Logging Settings",
render: LoggingSettingsPage,
})
}
/>
<TableSwitchRow
label="Add to Sidebar"
subLabel="Show plugin in Discord settings"
value={getStorage("addToSidebar", false)}
onValueChange={(value: boolean) => {
setStorage("addToSidebar", value);
forceUpdate();
}}
/>
</TableRowGroup>
>>>>>>> parent of ecd97c7 (feat: detach Sidebar patches as they)
{/* About */} {/* About */}
<TableRowGroup title="About"> <TableRowGroup title="About">