From 92d511c7b25ccdc3d32c57d3eb85de3efb4ef548 Mon Sep 17 00:00:00 2001 From: amsyarasyiq <82711525+amsyarasyiq@users.noreply.github.com> Date: Sun, 12 Feb 2023 18:18:46 +0800 Subject: [PATCH] Last.fm: functional settings page --- plugins/Last.fm/src/constants.ts | 11 +++++ plugins/Last.fm/src/index.ts | 72 ++++++++++++++++---------------- plugins/Last.fm/src/settings.tsx | 68 ++++++++++++++++++++++++++++-- plugins/Last.fm/src/types.ts | 14 +++++++ 4 files changed, 124 insertions(+), 41 deletions(-) create mode 100644 plugins/Last.fm/src/constants.ts create mode 100644 plugins/Last.fm/src/types.ts diff --git a/plugins/Last.fm/src/constants.ts b/plugins/Last.fm/src/constants.ts new file mode 100644 index 0000000..9211a02 --- /dev/null +++ b/plugins/Last.fm/src/constants.ts @@ -0,0 +1,11 @@ +export default { + /** The application ID of the plugin */ + APPLICATION_ID: "1054951789318909972", + /** Last.fm api key */ + LFM_API_KEY: "615322f0047e12aedbc610d9d71f7430", + /** These are the default album covers that are used by Last.fm */ + DEFAULT_COVER_HASHES: [ + "2a96cbd8b46e442fc41c2b86b821562f", + "c6f59c1e5e7240a4c0d427abd71f3dbb", + ] +} as const; diff --git a/plugins/Last.fm/src/index.ts b/plugins/Last.fm/src/index.ts index 865bfb7..27a5789 100644 --- a/plugins/Last.fm/src/index.ts +++ b/plugins/Last.fm/src/index.ts @@ -1,6 +1,7 @@ -import { logger, patcher, plugin } from "@vendetta"; +import { patcher, plugin } from "@vendetta"; import { findByProps, findByStoreName } from '@vendetta/metro'; import { FluxDispatcher } from '@vendetta/metro/common'; +import Constants from "./constants"; import Settings from "./settings"; let stopped: Boolean, @@ -9,25 +10,6 @@ let stopped: Boolean, lastTrackUrl: string, activityTypeUnpatch: () => void; -const settings = plugin.storage as { - appName: string; - username: string; - showTimestamp: string; - timeInterval: number; - listeningTo: boolean; - verboseLogging: boolean; -}; - -// Constants for the API calls (perhaps these should be options?) -const APPLICATION_ID = "1054951789318909972"; -const LFM_API_KEY = "615322f0047e12aedbc610d9d71f7430"; - -// These are the default album covers that are used by Last.fm -const DEFAULT_COVER_HASHES = [ - "2a96cbd8b46e442fc41c2b86b821562f", - "c6f59c1e5e7240a4c0d427abd71f3dbb", -]; - // Discord activity type Activity = { name: string; @@ -73,17 +55,19 @@ type Track = { loved: boolean; } +export const currentSettings = { ...plugin.storage } as PluginSettings; + // Discord modules that we need const UserStore = findByStoreName("UserStore") const SET_ACTIVITY = findByProps('SET_ACTIVITY').SET_ACTIVITY; -const verboseLog = (...message: any) => settings.verboseLogging && logger.log(...message); +const verboseLog = (...message: any) => currentSettings.verboseLogging && console.log(...message); async function fetchCurrentScrobble(): Promise { const params = new URLSearchParams({ 'method': 'user.getrecenttracks', - 'user': settings.username, - 'api_key': LFM_API_KEY, + 'user': currentSettings.username, + 'api_key': Constants.LFM_API_KEY, 'format': 'json', 'limit': '1', 'extended': '1' @@ -106,7 +90,7 @@ async function fetchCurrentScrobble(): Promise { // Currently ditches the default album covers (they're ugly) // Might as well use Youtube as fallback too (soontm) function filterAlbumCover(cover: string): string { - if (DEFAULT_COVER_HASHES.some(x => cover.includes(x))) { + if (Constants.DEFAULT_COVER_HASHES.some(x => cover.includes(x))) { return null; } return cover; @@ -133,24 +117,25 @@ async function sendRequest(activity: Activity): Promise { socket: { id: 120, application: { - id: APPLICATION_ID, + id: Constants.APPLICATION_ID, name: activity?.name || "RichPresence" }, transport: "ipc" }, args: { pid: 120, - activity: activity && { ...activity } + activity: activity && { ...activity } || null } }); } // Fetches the current scrobble and sets the activity // This is the main function that is called every 5 seconds (or whatever the user has set) -async function update() { +export async function update() { verboseLog("--> Fetching last track..."); const lastTrack = await fetchCurrentScrobble().catch((err) => { verboseLog("--> An error occurred while fetching the last track, aborting..."); + clearActivity(); throw err; }); @@ -167,15 +152,15 @@ async function update() { } const activity = { - name: settings.appName || "Music", - type: settings.listeningTo ? 2 : 0, + name: currentSettings.appName || "Music", + type: currentSettings.listeningTo ? 2 : 0, details: lastTrack.name, state: `by ${lastTrack.artist}`, } as Activity; lastTrackUrl = lastTrack.url; - if (settings.showTimestamp) { + if (currentSettings.showTimestamp) { activity.timestamps = { start: Date.now() / 1000 | 0 }; @@ -190,6 +175,7 @@ async function update() { const response = await sendRequest(activity).catch((err) => { verboseLog("--> An error occurred while setting the activity"); + clearActivity(); throw err; }); @@ -198,8 +184,17 @@ async function update() { return response; } +// Stops everything +export function flush(): Promise { + console.log("--> Flushing..."); + lastActivity = null; + updateInterval && clearInterval(updateInterval); + + return clearActivity(); +} + // Initializes the plugin -async function initialize() { +export async function initialize() { console.log("--> Initializing..."); stopped = false; @@ -209,10 +204,14 @@ async function initialize() { if (updateInterval) clearInterval(updateInterval); if (lastActivity) await clearActivity(); - update().catch(console.error); - let tries = 0; + update().catch((err) => { + console.error(err); + tries++; + }); + + // Periodically fetches the current scrobble and sets the activity updateInterval = setInterval( () => update() @@ -223,9 +222,10 @@ async function initialize() { if (++tries > 3) { console.error("Failed to fetch/set activity 3 times, aborting..."); clearInterval(updateInterval); + stopped = true; } }), - settings.timeInterval + currentSettings.timeInterval ); } @@ -277,10 +277,8 @@ export default { console.log("Stopping last.fm..."); stopped = true; - lastActivity = null; - updateInterval && clearInterval(updateInterval); - clearActivity(); + flush(); activityTypeUnpatch?.(); activityTypeUnpatch = null; }, diff --git a/plugins/Last.fm/src/settings.tsx b/plugins/Last.fm/src/settings.tsx index 2e2c8b8..40a7963 100644 --- a/plugins/Last.fm/src/settings.tsx +++ b/plugins/Last.fm/src/settings.tsx @@ -1,9 +1,69 @@ -import { React, ReactNative } from "@vendetta/metro/common"; +import { NavigationNative, React, ReactNative } from "@vendetta/metro/common"; +import { storage } from "@vendetta/plugin"; +import { useProxy } from "@vendetta/storage"; +import { Forms } from "@vendetta/ui/components"; +import { currentSettings, flush, initialize } from "."; + +const { ScrollView, TouchableOpacity } = ReactNative; +const { FormInput, FormDivider, FormSwitchRow, FormText } = Forms; export default function Settings() { + const settings = useProxy(storage) as PluginSettings; + const navigation = NavigationNative.useNavigation(); + + React.useEffect(() => { + navigation.setOptions({ + headerRight: () => ( + { + for (const key in storage) { + currentSettings[key] = settings[key]; + } + flush().then(() => initialize()).then(() => navigation.goBack()).catch(console.error); + }}> + UPDATE + + ) + }); + }, []); + return ( - - Hello World! - + + settings.appName = value.trim()} + title="Application Name" + placeholder="Music" + returnKeyType="done" + /> + + settings.username = value.trim()} + title="Username" + placeholder="wumpus123" + returnKeyType="done" + /> + + settings.showTimestamp = value} + /> + + settings.listeningTo = value} + /> + + settings.verboseLogging = value} + /> + ) } diff --git a/plugins/Last.fm/src/types.ts b/plugins/Last.fm/src/types.ts new file mode 100644 index 0000000..ac88193 --- /dev/null +++ b/plugins/Last.fm/src/types.ts @@ -0,0 +1,14 @@ +type PluginSettings = { + /** Application's name */ + appName: string; + /** User's last.fm username */ + username: string; + /** Whether or not to show the timestamp */ + showTimestamp: boolean; + /** The time interval between each update */ + timeInterval: number; + /** Whether or not to show "Listening to" */ + listeningTo: boolean; + /** Whether or not to show verbose logging */ + verboseLogging: boolean; +};