diff --git a/plugins/Last.fm/manifest.json b/plugins/Last.fm/manifest.json
index 6a9f6d3..9361d91 100644
--- a/plugins/Last.fm/manifest.json
+++ b/plugins/Last.fm/manifest.json
@@ -1,7 +1,8 @@
{
- "name": "Last.fm",
- "description": "Show off your last.fm status on your Discord profile.",
- "vendetta": {
- "icon": "ic_stage_music"
- }
+ "name": "Last.fm",
+ "description": "Show off your last.fm status on your Discord profile.",
+ "main": "src/index.tsx",
+ "vendetta": {
+ "icon": "ic_stage_music"
+ }
}
diff --git a/plugins/Last.fm/src/index.ts b/plugins/Last.fm/src/index.ts
deleted file mode 100644
index 5d51281..0000000
--- a/plugins/Last.fm/src/index.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { plugin } from "@vendetta";
-import { FluxDispatcher } from "@vendetta/metro/common";
-
-import { lazy } from "react";
-import { Activity, LFMSettings } from "../../defs";
-import { flush, initialize } from "./manager";
-import { UserStore } from "./modules";
-
-export const pluginState = {} as {
- pluginStopped?: boolean,
- lastActivity?: Activity,
- updateInterval?: NodeJS.Timer,
- lastTrackUrl?: string,
-};
-
-plugin.storage.ignoreSpotify ??= true;
-export const currentSettings = { ...plugin.storage } as LFMSettings;
-
-export default {
- settings: lazy(() => import("./ui/pages/Settings")),
- onLoad() {
- pluginState.pluginStopped = false;
-
- if (UserStore.getCurrentUser()) {
- initialize().catch(console.error);
- } else {
- const callback = () => {
- initialize().catch(console.error);
- FluxDispatcher.unsubscribe(callback);
- };
-
- FluxDispatcher.subscribe("CONNECTION_OPEN", callback);
- }
-
- },
- onUnload() {
- pluginState.pluginStopped = true;
- flush();
- }
-};
diff --git a/plugins/Last.fm/src/index.tsx b/plugins/Last.fm/src/index.tsx
new file mode 100644
index 0000000..9ddf6dd
--- /dev/null
+++ b/plugins/Last.fm/src/index.tsx
@@ -0,0 +1,121 @@
+import { plugin } from "@vendetta";
+import { FluxDispatcher, React } from "@vendetta/metro/common";
+
+import { lazy, Suspense } from "react";
+import { Activity, LFMSettings } from "../../defs";
+import { flush, initialize } from "./manager";
+import { UserStore } from "./modules";
+import { Forms } from "@vendetta/ui/components";
+import { ScrollView } from "react-native";
+
+const { FormText } = Forms;
+
+// Error boundary component for settings
+function SettingsErrorBoundary({ children }: { children: React.ReactNode }) {
+ const [hasError, setHasError] = React.useState(false);
+
+ React.useEffect(() => {
+ const errorHandler = (error: any) => {
+ console.error("Settings component error:", error);
+ setHasError(true);
+ };
+
+ return () => {
+ setHasError(false);
+ };
+ }, []);
+
+ if (hasError) {
+ return (
+
+
+ Failed to load Last.fm settings. Please try reloading the plugin.
+
+
+ );
+ }
+
+ return (
+
+ Loading Last.fm settings...
+
+ }>
+ {children}
+
+ );
+}
+
+// Lazy-loaded Settings component with error handling
+const LazySettings = lazy(() => {
+ try {
+ return import("./ui/pages/Settings").catch((error) => {
+ console.error("Failed to load Settings component:", error);
+ // Return a fallback component
+ return {
+ default: () => (
+
+
+ Error: Could not load Last.fm settings component.
+ {"\n"}Please check console for details.
+
+
+ )
+ };
+ });
+ } catch (error) {
+ console.error("Critical error in lazy loading:", error);
+ // Return fallback component for critical errors
+ return Promise.resolve({
+ default: () => (
+
+
+ Critical error loading settings. Please restart the app.
+
+
+ )
+ });
+ }
+});
+
+// Wrapped settings component
+function WrappedSettings() {
+ return (
+
+
+
+ );
+}
+
+export const pluginState = {} as {
+ pluginStopped?: boolean,
+ lastActivity?: Activity,
+ updateInterval?: NodeJS.Timer,
+ lastTrackUrl?: string,
+};
+
+plugin.storage.ignoreSpotify ??= true;
+export const currentSettings = { ...plugin.storage } as LFMSettings;
+
+export default {
+ settings: WrappedSettings,
+ onLoad() {
+ pluginState.pluginStopped = false;
+
+ if (UserStore.getCurrentUser()) {
+ initialize().catch(console.error);
+ } else {
+ const callback = () => {
+ initialize().catch(console.error);
+ FluxDispatcher.unsubscribe(callback);
+ };
+
+ FluxDispatcher.subscribe("CONNECTION_OPEN", callback);
+ }
+
+ },
+ onUnload() {
+ pluginState.pluginStopped = true;
+ flush();
+ }
+};
diff --git a/plugins/Last.fm/src/ui/pages/Debug.tsx b/plugins/Last.fm/src/ui/pages/Debug.tsx
index 07b4917..43aa31f 100644
--- a/plugins/Last.fm/src/ui/pages/Debug.tsx
+++ b/plugins/Last.fm/src/ui/pages/Debug.tsx
@@ -1,15 +1,52 @@
-import { Codeblock } from "@vendetta/ui/components";
-import { ScrollView } from "react-native";
-import { useDebugInfo } from "../../utils/debug";
+import { React } from "@vendetta/metro/common";
+import { Codeblock, Forms } from "@vendetta/ui/components";
+import { ScrollView, Text } from "react-native";
+import { useEffect } from "react";
+import {
+ useDebugInfo,
+ logComponentMount,
+ logComponentError,
+} from "../../utils/debug";
-export default function Debug() {
+const { FormText } = Forms;
+
+export default React.memo(function Debug() {
+ // Log component mount
+ useEffect(() => {
+ logComponentMount("Debug");
+ }, []);
+
+ try {
const debugInfo = useDebugInfo();
- return (
+ // Safety check for debugInfo
+ if (!debugInfo) {
+ logComponentError("Debug", "useDebugInfo returned null or undefined");
+ return (
-
- {debugInfo}
-
+
+ No debug information available
+
+ );
+ }
+
+ return (
+
+
+ {debugInfo}
+
+
);
-}
+ } catch (error) {
+ logComponentError("Debug", error);
+ console.error("Debug component error:", error);
+ return (
+
+
+ Error loading debug information: {String(error)}
+
+
+ );
+ }
+});
diff --git a/plugins/Last.fm/src/ui/pages/Settings.tsx b/plugins/Last.fm/src/ui/pages/Settings.tsx
index a06fd44..bf62a28 100644
--- a/plugins/Last.fm/src/ui/pages/Settings.tsx
+++ b/plugins/Last.fm/src/ui/pages/Settings.tsx
@@ -11,101 +11,152 @@ import { currentSettings } from "../..";
import { LFMSettings } from "../../../../defs";
import Constants from "../../constants";
import { initialize } from "../../manager";
+import {
+ logComponentMount,
+ logComponentError,
+ logNavigationError,
+ incrementSettingsLoad,
+} from "../../utils/debug";
-const { FormRow, FormInput, FormDivider, FormSwitchRow, FormText, FormIcon } = Forms;
+const { FormRow, FormInput, FormDivider, FormSwitchRow, FormText, FormIcon } =
+ Forms;
function UpdateButton() {
- async function onPressCallback() {
- for (const key in storage) {
- if (storage[key] != null) {
- currentSettings[key] = storage[key];
- }
+ async function onPressCallback() {
+ try {
+ incrementSettingsLoad();
+ for (const key in storage) {
+ if (storage[key] != null) {
+ currentSettings[key] = storage[key];
}
+ }
- await initialize();
- showToast("Settings updated!", getAssetIDByName("Check"));
+ await initialize();
+ showToast("Settings updated!", getAssetIDByName("Check"));
+ } catch (error) {
+ logComponentError("UpdateButton", error);
+ showToast("Failed to update settings", getAssetIDByName("Small"));
}
+ }
- return
- UPDATE
- ;
+ return (
+
+ UPDATE
+
+ );
}
export default React.memo(function Settings() {
- const settings = useProxy(storage) as LFMSettings;
- const navigation = NavigationNative.useNavigation();
+ const settings = useProxy(storage) as LFMSettings;
+ const navigation = NavigationNative.useNavigation();
- useEffect(() => {
- navigation.setOptions({
- title: "Last.fm Configuration",
- headerRight: UpdateButton
- });
- }, []);
+ // Log component mount
+ useEffect(() => {
+ logComponentMount("Settings");
+ }, []);
+ // Safety check to ensure settings is properly initialized
+ if (!settings) {
+ logComponentError("Settings", "Settings proxy is null or undefined");
return (
-
- settings.appName = value.trim()}
- title="Discord Application Name"
- placeholder={Constants.DEFAULT_APP_NAME}
- returnKeyType="done"
- />
-
- settings.username = value.trim()}
- title="Last.fm username"
- helpText={!settings.username && {"This field is required!"}}
- placeholder="wumpus123"
- returnKeyType="done"
- />
-
- settings.timeInterval = value}
- title="Update interval (in seconds)"
- placeholder={Constants.DEFAULT_TIME_INTERVAL.toString()}
- keyboardType="numeric"
- returnKeyType="done"
- />
-
- }
- value={settings.showTimestamp}
- onValueChange={(value: boolean) => settings.showTimestamp = value}
- />
-
- }
- value={settings.listeningTo}
- onValueChange={(value: boolean) => settings.listeningTo = value}
- />
-
- }
- value={settings.ignoreSpotify}
- onValueChange={(value: boolean) => settings.ignoreSpotify = value}
- />
-
- }
- trailing={FormRow.Arrow}
- onPress={() => {
- navigation.push("VendettaCustomPage", {
- title: "Debug",
- render: lazy(() => import("./Debug"))
- });
- }}
- />
-
+
+ Loading...
+
);
+ }
+
+ useEffect(() => {
+ try {
+ navigation.setOptions({
+ title: "Last.fm Configuration",
+ headerRight: () => ,
+ });
+ } catch (error) {
+ logComponentError("Settings-Navigation", error);
+ }
+ }, []);
+
+ return (
+
+ (settings.appName = value.trim())}
+ title="Discord Application Name"
+ placeholder={Constants.DEFAULT_APP_NAME}
+ returnKeyType="done"
+ />
+
+ (settings.username = value.trim())}
+ title="Last.fm username"
+ helpText={
+ !settings.username && (
+
+ {"This field is required!"}
+
+ )
+ }
+ placeholder="wumpus123"
+ returnKeyType="done"
+ />
+
+ (settings.timeInterval = value)}
+ title="Update interval (in seconds)"
+ placeholder={Constants.DEFAULT_TIME_INTERVAL.toString()}
+ keyboardType="numeric"
+ returnKeyType="done"
+ />
+
+ }
+ value={settings.showTimestamp}
+ onValueChange={(value: boolean) => (settings.showTimestamp = value)}
+ />
+
+ }
+ value={settings.listeningTo}
+ onValueChange={(value: boolean) => (settings.listeningTo = value)}
+ />
+
+
+ }
+ value={settings.ignoreSpotify}
+ onValueChange={(value: boolean) => (settings.ignoreSpotify = value)}
+ />
+
+ }
+ trailing={FormRow.Arrow}
+ onPress={() => {
+ try {
+ navigation.push("VendettaCustomPage", {
+ title: "Debug",
+ render: lazy(() => import("./Debug")),
+ });
+ } catch (error) {
+ logNavigationError(error);
+ console.error("Failed to navigate to Debug page:", error);
+ showToast("Failed to open debug page", getAssetIDByName("Small"));
+ }
+ }}
+ />
+
+ );
});
diff --git a/plugins/Last.fm/src/utils/debug.ts b/plugins/Last.fm/src/utils/debug.ts
index ab6e029..9646054 100644
--- a/plugins/Last.fm/src/utils/debug.ts
+++ b/plugins/Last.fm/src/utils/debug.ts
@@ -4,18 +4,87 @@ import { Activity, Track } from "../../../defs";
let __forceUpdate: () => void;
const debugInfo = {} as {
- lastActivity?: Activity,
- lastTrack?: Track,
- lastAPIResponse?: any,
- isSpotifyIgnored?: boolean,
+ lastActivity?: Activity;
+ lastTrack?: Track;
+ lastAPIResponse?: any;
+ isSpotifyIgnored?: boolean;
+ componentMountErrors?: string[];
+ lastReanimatedError?: string;
+ componentMountCount?: number;
+ settingsLoadAttempts?: number;
+ lastNavigationError?: string;
};
-export function setDebugInfo(key: keyof typeof debugInfo, value: typeof debugInfo[typeof key]) {
- debugInfo[key] = value;
- __forceUpdate?.();
+// Initialize debug counters
+debugInfo.componentMountErrors = [];
+debugInfo.componentMountCount = 0;
+debugInfo.settingsLoadAttempts = 0;
+
+export function setDebugInfo(
+ key: keyof typeof debugInfo,
+ value: (typeof debugInfo)[typeof key],
+) {
+ debugInfo[key] = value;
+ __forceUpdate?.();
+}
+
+export function logComponentMount(componentName: string) {
+ debugInfo.componentMountCount = (debugInfo.componentMountCount || 0) + 1;
+ console.log(
+ `[Last.fm Debug] Component mounted: ${componentName} (count: ${debugInfo.componentMountCount})`,
+ );
+}
+
+export function logComponentError(componentName: string, error: any) {
+ const errorMessage = `${componentName}: ${String(error)}`;
+ debugInfo.componentMountErrors = debugInfo.componentMountErrors || [];
+ debugInfo.componentMountErrors.push(errorMessage);
+
+ if (String(error).includes("Reanimated")) {
+ debugInfo.lastReanimatedError = errorMessage;
+ }
+
+ console.error(`[Last.fm Debug] Component error in ${componentName}:`, error);
+ __forceUpdate?.();
+}
+
+export function logNavigationError(error: any) {
+ debugInfo.lastNavigationError = String(error);
+ console.error(`[Last.fm Debug] Navigation error:`, error);
+ __forceUpdate?.();
+}
+
+export function incrementSettingsLoad() {
+ debugInfo.settingsLoadAttempts = (debugInfo.settingsLoadAttempts || 0) + 1;
+ console.log(
+ `[Last.fm Debug] Settings load attempt: ${debugInfo.settingsLoadAttempts}`,
+ );
}
export function useDebugInfo(): string {
+ try {
[, __forceUpdate] = useReducer((x) => ~x, 0);
- return JSON.stringify(debugInfo, null, 4);
+
+ // Add runtime information
+ const runtimeInfo = {
+ ...debugInfo,
+ timestamp: new Date().toISOString(),
+ reactVersion: require("react").version,
+ hasReanimatedError: !!debugInfo.lastReanimatedError,
+ totalErrors: debugInfo.componentMountErrors?.length || 0,
+ };
+
+ return JSON.stringify(runtimeInfo, null, 4);
+ } catch (error) {
+ console.error("[Last.fm Debug] Error in useDebugInfo:", error);
+ return JSON.stringify(
+ {
+ error: "Failed to generate debug info",
+ errorMessage: String(error),
+ timestamp: new Date().toISOString(),
+ },
+ null,
+ 4,
+ );
+ }
}