Last.fm: Major refactoring
This commit is contained in:
parent
7d6ce4055d
commit
646bc0b40b
8 changed files with 203 additions and 130 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -6,3 +6,4 @@ yarn.lock
|
|||
package-lock.json
|
||||
Themer
|
||||
RealThemer
|
||||
ChatMesser
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ async function buildPlugin(plugin) {
|
|||
format: "iife",
|
||||
compact: true,
|
||||
exports: "named",
|
||||
inlineDynamicImports: true,
|
||||
},
|
||||
onwarn: (warning) => {
|
||||
![
|
||||
|
|
|
|||
|
|
@ -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 <TouchableOpacity onPress={onPressCallback}>
|
||||
<FormText style={{ marginRight: 12 }}>UPDATE</FormText>
|
||||
</TouchableOpacity>;
|
||||
}
|
||||
|
||||
export default function Settings() {
|
||||
const settings = useProxy(storage) as LFMSettings;
|
||||
const navigation = NavigationNative.useNavigation();
|
||||
|
||||
useEffect(() => {
|
||||
navigation.setOptions({
|
||||
title: "Last.fm Configuration",
|
||||
headerRight: UpdateButton
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ScrollView>
|
||||
<FormInput
|
||||
value={settings.appName || undefined}
|
||||
onChangeText={(value: string) => settings.appName = value.trim()}
|
||||
title="Discord Application Name"
|
||||
placeholder={Constants.DEFAULT_APP_NAME}
|
||||
returnKeyType="done"
|
||||
/>
|
||||
<FormDivider />
|
||||
<FormInput required
|
||||
value={settings.username || undefined}
|
||||
onChangeText={(value: string) => settings.username = value.trim()}
|
||||
title="Last.fm username"
|
||||
helpText={!settings.username && <Text style={{ color: "#FF0000" }}>{"This field is required!"}</Text>}
|
||||
placeholder="wumpus123"
|
||||
returnKeyType="done"
|
||||
/>
|
||||
<FormDivider />
|
||||
<FormInput
|
||||
value={settings.timeInterval}
|
||||
onChangeText={(value: string) => settings.timeInterval = value}
|
||||
title="Update interval (in seconds)"
|
||||
placeholder={Constants.DEFAULT_TIME_INTERVAL.toString()}
|
||||
keyboardType="numeric"
|
||||
returnKeyType="done"
|
||||
/>
|
||||
<FormDivider />
|
||||
<FormSwitchRow
|
||||
label="Show time elapsed"
|
||||
subLabel="Show the time elapsed since the song started playing"
|
||||
leading={<FormIcon source={getAssetIDByName("clock")} />}
|
||||
value={settings.showTimestamp}
|
||||
onValueChange={(value: boolean) => settings.showTimestamp = value}
|
||||
/>
|
||||
<FormDivider />
|
||||
<FormSwitchRow
|
||||
label="Set status as listening"
|
||||
subLabel='Set your status as "Listening to" instead of "Playing"'
|
||||
leading={<FormIcon source={getAssetIDByName("ic_headset_neutral")} />}
|
||||
value={settings.listeningTo}
|
||||
onValueChange={(value: boolean) => settings.listeningTo = value}
|
||||
/>
|
||||
<FormDivider />
|
||||
<FormSwitchRow
|
||||
label="Hide when Spotify is running"
|
||||
subLabel="Hide the status when a Spotify activity is detected"
|
||||
leading={<FormIcon source={getAssetIDByName("img_account_sync_spotify_light_and_dark")} />}
|
||||
value={settings.ignoreSpotify}
|
||||
onValueChange={(value: boolean) => settings.ignoreSpotify = value}
|
||||
/>
|
||||
<FormDivider />
|
||||
<FormSwitchRow
|
||||
label="Verbose logging"
|
||||
subLabel="Log more information to the console for debugging purposes"
|
||||
leading={<FormIcon source={getAssetIDByName("pencil")} />}
|
||||
value={settings.verboseLogging}
|
||||
onValueChange={(value: boolean) => settings.verboseLogging = value}
|
||||
/>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
18
plugins/Last.fm/src/modules.ts
Normal file
18
plugins/Last.fm/src/modules.ts
Normal file
|
|
@ -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<string[]>;
|
||||
[key: PropertyKey]: any;
|
||||
};
|
||||
|
||||
export const SelfPresenceStore = findByStoreName("SelfPresenceStore") as {
|
||||
getActivities: () => any[];
|
||||
findActivity: (filter: (activity) => boolean) => any;
|
||||
};
|
||||
|
||||
export const UserStore = findByStoreName("UserStore");
|
||||
12
plugins/Last.fm/src/pages/Debug.tsx
Normal file
12
plugins/Last.fm/src/pages/Debug.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { Codeblock } from "@vendetta/ui/components";
|
||||
import { ScrollView } from "react-native";
|
||||
|
||||
export default function Debug() {
|
||||
return (
|
||||
<ScrollView>
|
||||
<Codeblock selectable>
|
||||
{JSON.stringify({ blah: "bleh" }, null, 4)}
|
||||
</Codeblock>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
158
plugins/Last.fm/src/pages/Settings.tsx
Normal file
158
plugins/Last.fm/src/pages/Settings.tsx
Normal file
|
|
@ -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 <TouchableOpacity onPress={onPressCallback}>
|
||||
<FormText style={{ marginRight: 12 }}>UPDATE</FormText>
|
||||
</TouchableOpacity>;
|
||||
}
|
||||
|
||||
type FormRowProps = {
|
||||
label: string;
|
||||
subLabel: string;
|
||||
leading: React.ReactNode;
|
||||
trailing: React.ReactNode;
|
||||
onPress: () => void;
|
||||
};
|
||||
|
||||
type RowProps = Partial<FormRowProps & ({
|
||||
type: "switch";
|
||||
value: boolean;
|
||||
onValueChange: (value: boolean) => 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 <FormSwitchRow {...props} />;
|
||||
case "input": return <FormInput {...props} />;
|
||||
default: return <FormRow {...props} />;
|
||||
}
|
||||
};
|
||||
|
||||
return <FlatList
|
||||
data={rows}
|
||||
renderItem={({ item }) => <Row {...item} />}
|
||||
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 (
|
||||
<ScrollView>
|
||||
<Rows rows={[
|
||||
{
|
||||
type: "input",
|
||||
title: "Discord Application Name",
|
||||
value: settings.appName || void 0,
|
||||
onChangeText: (value: string) => 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: <FormIcon source={getAssetIDByName("clock")} />,
|
||||
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: <FormIcon source={getAssetIDByName("ic_headset_neutral")} />,
|
||||
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: <FormIcon source={getAssetIDByName("img_account_sync_spotify_light_and_dark")} />,
|
||||
value: settings.ignoreSpotify,
|
||||
onValueChange: (value: boolean) => settings.ignoreSpotify = value
|
||||
},
|
||||
{
|
||||
label: "Debug",
|
||||
subLabel: "View debug information",
|
||||
leading: <FormIcon source={getAssetIDByName("debug")} />,
|
||||
trailing: FormRow.Arrow,
|
||||
onPress: async () => {
|
||||
const imported = await import("./Debug");
|
||||
|
||||
navigation.push("VendettaCustomPage", {
|
||||
title: "Debug",
|
||||
render: imported.default
|
||||
});
|
||||
}
|
||||
}
|
||||
]} />
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
|
@ -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<string[]>;
|
||||
[key: PropertyKey]: any;
|
||||
};
|
||||
const PresenceStore = findByStoreName("PresenceStore");
|
||||
import { AssetManager, SelfPresenceStore } from "./modules";
|
||||
|
||||
enum ActivityType {
|
||||
PLAYING = 0,
|
||||
|
|
@ -43,7 +37,7 @@ async function fetchLatestScrobble(): Promise<Track> {
|
|||
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,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue