diff --git a/.gitignore b/.gitignore
index 406520b..ab8c6c5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ yarn.lock
package-lock.json
Themer
RealThemer
+ChatMesser
diff --git a/build.mjs b/build.mjs
index fe19c58..8fb409a 100644
--- a/build.mjs
+++ b/build.mjs
@@ -68,6 +68,7 @@ async function buildPlugin(plugin) {
format: "iife",
compact: true,
exports: "named",
+ inlineDynamicImports: true,
},
onwarn: (warning) => {
![
diff --git a/plugins/Last.fm/src/Settings.tsx b/plugins/Last.fm/src/Settings.tsx
deleted file mode 100644
index 128defd..0000000
--- a/plugins/Last.fm/src/Settings.tsx
+++ /dev/null
@@ -1,107 +0,0 @@
-import { NavigationNative, React } from "@vendetta/metro/common";
-import { storage } from "@vendetta/plugin";
-import { useProxy } from "@vendetta/storage";
-import { getAssetIDByName } from "@vendetta/ui/assets";
-import { Forms } from "@vendetta/ui/components";
-import { showToast } from "@vendetta/ui/toasts";
-
-import { currentSettings } from ".";
-import Constants from "./constants";
-import { initialize } from "./utils";
-
-import { useEffect } from "react";
-import { ScrollView, Text, TouchableOpacity } from "react-native";
-
-const { FormInput, FormDivider, FormSwitchRow, FormText, FormIcon } = Forms;
-
-function UpdateButton() {
- async function onPressCallback() {
- for (const key in storage) {
- if (storage[key] === false || storage[key]) {
- currentSettings[key] = storage[key];
- }
- }
-
- console.log("Applying settings...");
- await initialize();
- showToast("Settings updated!", getAssetIDByName("Check"));
- }
-
- return
- UPDATE
- ;
-}
-
-export default function Settings() {
- const settings = useProxy(storage) as LFMSettings;
- const navigation = NavigationNative.useNavigation();
-
- useEffect(() => {
- navigation.setOptions({
- title: "Last.fm Configuration",
- headerRight: UpdateButton
- });
- }, []);
-
- 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}
- />
-
- }
- value={settings.verboseLogging}
- onValueChange={(value: boolean) => settings.verboseLogging = value}
- />
-
- );
-}
diff --git a/plugins/Last.fm/src/index.ts b/plugins/Last.fm/src/index.ts
index 3f1a4a9..f3958ae 100644
--- a/plugins/Last.fm/src/index.ts
+++ b/plugins/Last.fm/src/index.ts
@@ -1,8 +1,9 @@
import { plugin } from "@vendetta";
-import { findByProps, findByStoreName } from "@vendetta/metro";
import { FluxDispatcher } from "@vendetta/metro/common";
-import Settings from "./Settings";
+import { Activity, LFMSettings } from "../../defs";
+import { UserStore } from "./modules";
+import Settings from "./pages/Settings";
import { flush, initialize } from "./utils";
export const global = {} as {
@@ -15,10 +16,6 @@ export const global = {} as {
plugin.storage.ignoreSpotify ??= true;
export const currentSettings = { ...plugin.storage } as LFMSettings;
-// Discord modules that we need
-export const UserStore = findByStoreName("UserStore");
-export const { SET_ACTIVITY } = findByProps("SET_ACTIVITY");
-
export const verboseLog = (...message: any) => currentSettings.verboseLogging && console.log(...message);
// Plugin entry point
diff --git a/plugins/Last.fm/src/modules.ts b/plugins/Last.fm/src/modules.ts
new file mode 100644
index 0000000..9cb29da
--- /dev/null
+++ b/plugins/Last.fm/src/modules.ts
@@ -0,0 +1,18 @@
+import { findByProps, findByStoreName } from "@vendetta/metro";
+
+// We don't really *need* this module, BUT this module has to be initialized before we can dispatch LOCAL_ACTIVITY_UPDATE
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+const { SET_ACTIVITY } = findByProps("SET_ACTIVITY");
+
+export const AssetManager = findByProps("getAssetIds") as {
+ getAssetIds: (appId: string, ids: string[]) => string[];
+ fetchAssetIds: (appId: string, ids: string[]) => Promise;
+ [key: PropertyKey]: any;
+};
+
+export const SelfPresenceStore = findByStoreName("SelfPresenceStore") as {
+ getActivities: () => any[];
+ findActivity: (filter: (activity) => boolean) => any;
+};
+
+export const UserStore = findByStoreName("UserStore");
diff --git a/plugins/Last.fm/src/pages/Debug.tsx b/plugins/Last.fm/src/pages/Debug.tsx
new file mode 100644
index 0000000..0264519
--- /dev/null
+++ b/plugins/Last.fm/src/pages/Debug.tsx
@@ -0,0 +1,12 @@
+import { Codeblock } from "@vendetta/ui/components";
+import { ScrollView } from "react-native";
+
+export default function Debug() {
+ return (
+
+
+ {JSON.stringify({ blah: "bleh" }, null, 4)}
+
+
+ );
+}
diff --git a/plugins/Last.fm/src/pages/Settings.tsx b/plugins/Last.fm/src/pages/Settings.tsx
new file mode 100644
index 0000000..7e61511
--- /dev/null
+++ b/plugins/Last.fm/src/pages/Settings.tsx
@@ -0,0 +1,158 @@
+import { NavigationNative, React } from "@vendetta/metro/common";
+import { storage } from "@vendetta/plugin";
+import { useProxy } from "@vendetta/storage";
+import { getAssetIDByName } from "@vendetta/ui/assets";
+import { Forms } from "@vendetta/ui/components";
+import { showToast } from "@vendetta/ui/toasts";
+
+import { currentSettings } from "..";
+import Constants from "../constants";
+import { initialize } from "../utils";
+
+import { FlatList, ScrollView, TouchableOpacity } from "react-native";
+import { LFMSettings } from "../../../defs";
+
+const { FormInput, FormDivider, FormSwitchRow, FormText, FormIcon, FormRow } = Forms;
+
+function UpdateButton() {
+ async function onPressCallback() {
+ for (const key in storage) {
+ if (storage[key] === false || storage[key]) {
+ currentSettings[key] = storage[key];
+ }
+ }
+
+ console.log("Applying settings...");
+ await initialize();
+ showToast("Settings updated!", getAssetIDByName("Check"));
+ }
+
+ return
+ UPDATE
+ ;
+}
+
+type FormRowProps = {
+ label: string;
+ subLabel: string;
+ leading: React.ReactNode;
+ trailing: React.ReactNode;
+ onPress: () => void;
+};
+
+type RowProps = Partial void;
+} | {
+ label: never;
+ subLabel: never;
+
+ type: "input";
+ title: string;
+ value: string;
+ required?: boolean;
+ onChangeText: (value: string) => void;
+ placeholder: string;
+ keyboardType?: "default" | "numeric";
+ returnKeyType?: "done";
+})>;
+
+function Rows({ rows }: { rows: RowProps[] }) {
+ const Row = ({ type, ...props }: RowProps) => {
+ switch (type) {
+ case "switch": return ;
+ case "input": return ;
+ default: return ;
+ }
+ };
+
+ return
}
+ keyExtractor={(item) => item.label}
+ ItemSeparatorComponent={FormDivider}
+ />;
+}
+
+export default function Settings() {
+ const settings = useProxy(storage) as LFMSettings;
+ const navigation = NavigationNative.useNavigation();
+
+ navigation.addListener("focus", () => {
+ navigation.setOptions({
+ headerRight: UpdateButton
+ });
+ });
+
+ return (
+
+ settings.appName = value.trim(),
+ placeholder: Constants.DEFAULT_APP_NAME,
+ returnKeyType: "done"
+ },
+ {
+ type: "input",
+ title: "Last.fm username",
+ value: settings.username || void 0,
+ onChangeText: (value: string) => settings.username = value.trim(),
+ placeholder: "wumpus123",
+ returnKeyType: "done",
+ required: true
+ },
+ {
+ type: "input",
+ title: "Update interval (in seconds)",
+ value: String(settings.timeInterval),
+ onChangeText: (value: string) => settings.timeInterval = value,
+ placeholder: Constants.DEFAULT_TIME_INTERVAL.toString(),
+ keyboardType: "numeric",
+ returnKeyType: "done"
+ },
+ {
+ type: "switch",
+ label: "Show time elapsed",
+ subLabel: "Show the time elapsed since the song started playing",
+ leading: ,
+ value: settings.showTimestamp,
+ onValueChange: (value: boolean) => settings.showTimestamp = value
+ },
+ {
+ type: "switch",
+ label: "Set status as listening",
+ subLabel: "Set your status as \"Listening to\" instead of \"Playing\"",
+ leading: ,
+ value: settings.listeningTo,
+ onValueChange: (value: boolean) => settings.listeningTo = value
+ },
+ {
+ type: "switch",
+ label: "Hide when Spotify is running",
+ subLabel: "Hide the status when a Spotify activity is detected",
+ leading: ,
+ value: settings.ignoreSpotify,
+ onValueChange: (value: boolean) => settings.ignoreSpotify = value
+ },
+ {
+ label: "Debug",
+ subLabel: "View debug information",
+ leading: ,
+ trailing: FormRow.Arrow,
+ onPress: async () => {
+ const imported = await import("./Debug");
+
+ navigation.push("VendettaCustomPage", {
+ title: "Debug",
+ render: imported.default
+ });
+ }
+ }
+ ]} />
+
+ );
+}
diff --git a/plugins/Last.fm/src/utils.ts b/plugins/Last.fm/src/utils.ts
index bdf236f..eca3a02 100644
--- a/plugins/Last.fm/src/utils.ts
+++ b/plugins/Last.fm/src/utils.ts
@@ -1,16 +1,10 @@
-import { findByProps, findByStoreName } from "@vendetta/metro";
import { FluxDispatcher } from "@vendetta/metro/common";
import { getAssetIDByName } from "@vendetta/ui/assets";
import { showToast } from "@vendetta/ui/toasts";
-import { UserStore, currentSettings, global, verboseLog } from ".";
+import { currentSettings, global, verboseLog } from ".";
+import { Activity, Track } from "../../defs";
import Constants from "./constants";
-
-const AssetManager = findByProps("getAssetIds") as {
- getAssetIds: (appId: string, ids: string[]) => string[];
- fetchAssetIds: (appId: string, ids: string[]) => Promise;
- [key: PropertyKey]: any;
-};
-const PresenceStore = findByStoreName("PresenceStore");
+import { AssetManager, SelfPresenceStore } from "./modules";
enum ActivityType {
PLAYING = 0,
@@ -43,7 +37,7 @@ async function fetchLatestScrobble(): Promise