From 646bc0b40bca150d018432c8c1502f90e9d5f5a1 Mon Sep 17 00:00:00 2001 From: amsyarasyiq <82711525+amsyarasyiq@users.noreply.github.com> Date: Sun, 9 Jul 2023 08:17:28 +0800 Subject: [PATCH] Last.fm: Major refactoring --- .gitignore | 1 + build.mjs | 1 + plugins/Last.fm/src/Settings.tsx | 107 ----------------- plugins/Last.fm/src/index.ts | 9 +- plugins/Last.fm/src/modules.ts | 18 +++ plugins/Last.fm/src/pages/Debug.tsx | 12 ++ plugins/Last.fm/src/pages/Settings.tsx | 158 +++++++++++++++++++++++++ plugins/Last.fm/src/utils.ts | 27 ++--- 8 files changed, 203 insertions(+), 130 deletions(-) delete mode 100644 plugins/Last.fm/src/Settings.tsx create mode 100644 plugins/Last.fm/src/modules.ts create mode 100644 plugins/Last.fm/src/pages/Debug.tsx create mode 100644 plugins/Last.fm/src/pages/Settings.tsx 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 { name: lastTrack.name, artist: lastTrack.artist.name, album: lastTrack.album["#text"], - albumArt: await handleAlbumCover(lastTrack.image[3]["#text"]), + albumArt: await handleAlbumCover(lastTrack.image?.find((x: any) => x.size === "large")?.["#text"]), url: lastTrack.url, date: lastTrack.date?.["#text"] ?? "now", nowPlaying: Boolean(lastTrack["@attr"]?.nowplaying), @@ -111,12 +105,11 @@ async function update() { } if (currentSettings.ignoreSpotify) { - for (const activity of PresenceStore.getActivities(UserStore.getCurrentUser().id)) { - if (activity?.type === ActivityType.LISTENING && activity.application_id !== Constants.APPLICATION_ID) { - verboseLog("--> Spotify is currently playing, aborting..."); - clearActivity(); - return; - } + const spotifyActivity = SelfPresenceStore.findActivity(act => act.sync_id); + if (spotifyActivity) { + verboseLog("--> Spotify is currently playing, aborting..."); + clearActivity(); + return; } } @@ -142,7 +135,7 @@ async function update() { const activity = { name: currentSettings.appName || Constants.DEFAULT_APP_NAME, flags: 0, - type: currentSettings.listeningTo ? 2 : 0, + type: currentSettings.listeningTo ? ActivityType.LISTENING : ActivityType.PLAYING, details: lastTrack.name, state: `by ${lastTrack.artist}`, application_id: Constants.APPLICATION_ID,