diff --git a/chatboxavatar/index.tsx b/chatboxavatar/index.tsx
deleted file mode 100644
index afdba15..0000000
--- a/chatboxavatar/index.tsx
+++ /dev/null
@@ -1,178 +0,0 @@
-import { after } from "@api/patcher";
-import { findByNameLazy, findByProps, findByPropsLazy, findByStoreName, findByTypeDisplayName } from "@metro";
-import { ReactNative } from "@metro/common";
-import { definePlugin } from "@plugins";
-import { Contributors } from "@rain/Developers";
-import React, { useEffect, useRef } from "react";
-
-import ChatboxAvatarSettings from "./settings";
-import { useChatboxAvatarSettings } from "./storage";
-
-const Flux = findByProps("useStateFromStores");
-const ChatInputActions = findByTypeDisplayName("ChatInputActions");
-const ChatInputSendButton = findByTypeDisplayName("ChatInputSendButton");
-let hasText = false;
-let sendBtnRef: { setHasText?: (hasText: boolean) => void } | undefined;
-const { Pressable, View, Animated } = ReactNative;
-
-const avatarCollapse = new Animated.Value(0);
-
-const Avatar = findByPropsLazy("default", "AvatarSizes", "getStatusSize")?.default;
-
-const UserStore = findByStoreName("UserStore");
-const SelectedChannelStore = findByStoreName("SelectedChannelStore");
-const ChannelStore = findByStoreName("ChannelStore");
-const SelfPresenceStore = findByStoreName("SelfPresenceStore");
-const showUserProfileActionSheet = findByNameLazy("showUserProfileActionSheet");
-const showYouAccountActionSheetByProp = findByPropsLazy("showYouAccountActionSheet");
-
-function AvatarAction() {
- const [textState, setTextState] = React.useState(false);
- const self = Flux?.useStateFromStores?.([UserStore], () => UserStore?.getCurrentUser?.());
- const status = Flux?.useStateFromStores?.([SelfPresenceStore], () => SelfPresenceStore?.getStatus?.());
- const settings = useChatboxAvatarSettings();
- const channelId = Flux?.useStateFromStores?.([SelectedChannelStore], () => SelectedChannelStore?.getCurrentlySelectedChannelId?.());
- const channel = Flux?.useStateFromStores?.([ChannelStore], () => ChannelStore?.getChannel?.(channelId), [channelId]);
-
- const animated = useRef(avatarCollapse).current;
-
- React.useEffect(() => {
- const interval = setInterval(() => {
- setTextState(hasText);
- }, 100);
- return () => clearInterval(interval);
- }, []);
-
- useEffect(() => {
- const shouldCollapse = settings.collapseWhileTyping && textState;
- Animated.parallel([
- Animated.timing(animated, {
- toValue: shouldCollapse ? 1 : 0,
- duration: 200,
- useNativeDriver: false,
- }),
- ]).start();
- }, [textState, settings.collapseWhileTyping, animated]);
-
- if (!self) return null;
-
- const openAccountSheet = () => {
- const fn = showYouAccountActionSheetByProp?.showYouAccountActionSheet;
- if (typeof fn === "function") {
- try {
- fn(true, true);
- return;
- } catch (err) {
- }
- }
- showUserProfileActionSheet?.({ userId: self.id, channelId: channel?.id ?? channelId });
- };
-
- const handlePress = () => {
- switch (settings.pressAction) {
- case "profile":
- showUserProfileActionSheet?.({ userId: self.id, channelId: channel?.id ?? channelId });
- break;
- case "server":
- openAccountSheet();
- break;
- default:
- break;
- }
- };
-
- const handleLongPress = () => {
- switch (settings.longPressAction) {
- case "profile":
- showUserProfileActionSheet?.({ userId: self.id, channelId: channel?.id ?? channelId });
- break;
- case "server":
- openAccountSheet();
- break;
- default:
- break;
- }
- };
-
- return (
-
-
- {Avatar && (
-
- )}
-
-
- );
-}
-
-const unpatches: (() => void)[] = [];
-
-export default definePlugin({
- name: "ChatboxAvatar",
- description: "Adds your avatar to the chatbox.",
- author: [Contributors.LampDelivery],
- id: "chatboxavatar",
- version: "v1.0.0",
- requiresRestart: true,
- settings: ChatboxAvatarSettings,
- async start() {
- if (!ChatInputActions?.type || !ChatInputSendButton?.type) return;
- unpatches.push(
- after("render", ChatInputSendButton.type, (args, ret) => {
- setImmediate(() => {
- setImmediate(() => {
- if (args?.[1]?.current) {
- sendBtnRef = args[1].current;
- const origSetHasText = sendBtnRef?.setHasText;
- unpatches.push(() => { if (sendBtnRef && origSetHasText) sendBtnRef.setHasText = origSetHasText; });
- if (sendBtnRef) {
- sendBtnRef.setHasText = (hasText_: boolean) => {
- hasText = hasText_;
- if (origSetHasText) return origSetHasText.call(sendBtnRef, hasText_);
- };
- }
- }
- });
- });
- })
- );
-
- },
- async eagerStart() {
- if (!ChatInputActions?.type || !ChatInputSendButton?.type) return;
- unpatches.push(
- after("render", ChatInputActions.type, (args, ret) => {
- return React.createElement(
- View,
- { style: { flexDirection: "row", alignItems: "center" } },
- ret,
- React.createElement(AvatarAction)
- );
- })
- );
- },
- stop() {
- for (const unpatch of unpatches) unpatch?.();
- unpatches.length = 0;
- },
-});
diff --git a/chatboxavatar/settings.tsx b/chatboxavatar/settings.tsx
deleted file mode 100644
index 0c54d5b..0000000
--- a/chatboxavatar/settings.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import { Stack, TableRadioGroup, TableRadioRow } from "@metro/common/components";
-import React from "react";
-
-import { ChatboxAvatarSettings, useChatboxAvatarSettings } from "./storage";
-export default function ChatboxAvatarSettings() {
- const settings = useChatboxAvatarSettings();
- const [, forceUpdate] = React.useReducer(x => ~x, 0);
- const setPressAction = (value: string) => {
- useChatboxAvatarSettings.getState().updateSettings?.({ pressAction: value as ChatboxAvatarSettings["pressAction"] });
- forceUpdate();
- };
- const setLongPressAction = (value: string) => {
- useChatboxAvatarSettings.getState().updateSettings?.({ longPressAction: value as ChatboxAvatarSettings["longPressAction"] });
- forceUpdate();
- };
- return (
-
-
-
-
-
-
-
-
-
- {
- useChatboxAvatarSettings.getState().updateSettings?.({ showStatusCutout: v === "true" });
- forceUpdate();
- }}
- title="Status Icon"
- >
-
-
-
- {
- useChatboxAvatarSettings.getState().updateSettings?.({ collapseWhileTyping: v === "true" });
- forceUpdate();
- }}
- title="Collapse While Typing"
- >
-
-
-
-
- );
-}
diff --git a/chatboxavatar/storage.ts b/chatboxavatar/storage.ts
deleted file mode 100644
index 046f2c6..0000000
--- a/chatboxavatar/storage.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { createPluginStore } from "@api/storage";
-
-export interface ChatboxAvatarSettings {
- replaceButton: "gallery" | "apps" | "gift";
- pressAction: "profile" | "server";
- longPressAction: "profile" | "server";
- serverId?: string;
- showStatusCutout?: boolean;
- collapseWhileTyping?: boolean;
-}
-
-export const {
- useStore: useChatboxAvatarSettings,
- settings: chatboxAvatarSettings,
-} = createPluginStore("chatboxavatar", {
- replaceButton: "gallery",
- pressAction: "profile",
- longPressAction: "server",
- serverId: "",
- showStatusCutout: false,
- collapseWhileTyping: false,
-});
diff --git a/plugins/ChatboxAvatar/manifest.json b/plugins/ChatboxAvatar/manifest.json
deleted file mode 100644
index 8ebd537..0000000
--- a/plugins/ChatboxAvatar/manifest.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "Chatbox Avatar",
- "description": "Quickly view your profile from the chatbox.",
- "authors": [
- {
- "name": "Cynosphere",
- "id": "150745989836308480"
- },
- {
- "name": "kmmiio99o",
- "id": "879393496627306587"
- }
- ],
- "main": "src/index.tsx",
- "vendetta": {
- "icon": "UserSquareIcon"
- }
-}
diff --git a/plugins/ChatboxAvatar/src/index.tsx b/plugins/ChatboxAvatar/src/index.tsx
deleted file mode 100644
index d08b9e8..0000000
--- a/plugins/ChatboxAvatar/src/index.tsx
+++ /dev/null
@@ -1,165 +0,0 @@
-import { after } from "@vendetta/patcher";
-import { findByName, findByProps, findByStoreName, findByDisplayName } from "@vendetta/metro";
-import { ReactNative } from "@vendetta/metro/common";
-import { plugin } from "@vendetta";
-import React, { useEffect, useRef } from "react";
-
-import ChatboxAvatarSettings from "./settings";
-
-const Flux = findByProps("useStateFromStores");
-const ChatInputActions = findByDisplayName("ChatInputActions");
-const ChatInputSendButton = findByDisplayName("ChatInputSendButton");
-let hasText = false;
-let sendBtnRef: { setHasText?: (hasText: boolean) => void } | undefined;
-const { Pressable, View, Animated } = ReactNative;
-
-const avatarCollapse = new Animated.Value(0);
-
-const Avatar = findByProps("default", "AvatarSizes", "getStatusSize")?.default;
-
-const UserStore = findByStoreName("UserStore");
-const SelectedChannelStore = findByStoreName("SelectedChannelStore");
-const ChannelStore = findByStoreName("ChannelStore");
-const SelfPresenceStore = findByStoreName("SelfPresenceStore");
-const showUserProfileActionSheet = findByName("showUserProfileActionSheet");
-const showYouAccountActionSheetByProp = findByProps("showYouAccountActionSheet");
-
-function AvatarAction() {
- const [textState, setTextState] = React.useState(false);
- const self = Flux?.useStateFromStores?.([UserStore], () => UserStore?.getCurrentUser?.());
- const status = Flux?.useStateFromStores?.([SelfPresenceStore], () => SelfPresenceStore?.getStatus?.());
- const settings = plugin.storage;
- const channelId = Flux?.useStateFromStores?.([SelectedChannelStore], () => SelectedChannelStore?.getCurrentlySelectedChannelId?.());
- const channel = Flux?.useStateFromStores?.([ChannelStore], () => ChannelStore?.getChannel?.(channelId), [channelId]);
-
- const animated = useRef(avatarCollapse).current;
-
- React.useEffect(() => {
- const interval = setInterval(() => {
- setTextState(hasText);
- }, 100);
- return () => clearInterval(interval);
- }, []);
-
- useEffect(() => {
- const shouldCollapse = settings.collapseWhileTyping && textState;
- Animated.parallel([
- Animated.timing(animated, {
- toValue: shouldCollapse ? 1 : 0,
- duration: 200,
- useNativeDriver: false,
- }),
- ]).start();
- }, [textState, settings.collapseWhileTyping, animated]);
-
- if (!self) return null;
-
- const openAccountSheet = () => {
- const fn = showYouAccountActionSheetByProp?.showYouAccountActionSheet;
- if (typeof fn === "function") {
- try {
- fn(true, true);
- return;
- } catch {}
- }
- showUserProfileActionSheet?.({ userId: self.id, channelId: channel?.id ?? channelId });
- };
-
- const handlePress = () => {
- switch (settings.pressAction) {
- case "profile":
- showUserProfileActionSheet?.({ userId: self.id, channelId: channel?.id ?? channelId });
- break;
- case "server":
- openAccountSheet();
- break;
- default:
- break;
- }
- };
-
- const handleLongPress = () => {
- switch (settings.longPressAction) {
- case "profile":
- showUserProfileActionSheet?.({ userId: self.id, channelId: channel?.id ?? channelId });
- break;
- case "server":
- openAccountSheet();
- break;
- default:
- break;
- }
- };
-
- return (
-
-
- {Avatar && (
-
- )}
-
-
- );
-}
-
-const unpatches: (() => void)[] = [];
-
-export default {
- onLoad() {
- if (!ChatInputActions?.prototype?.render || !ChatInputSendButton?.prototype?.render) return;
- unpatches.push(
- after("render", ChatInputActions.prototype, (args, ret) => {
- return React.createElement(
- View,
- { style: { flexDirection: "row", alignItems: "center" } },
- ret,
- React.createElement(AvatarAction)
- );
- })
- );
- unpatches.push(
- after("render", ChatInputSendButton.prototype, (args, ret) => {
- setImmediate(() => {
- setImmediate(() => {
- if (args?.[1]?.current) {
- sendBtnRef = args[1].current;
- const origSetHasText = sendBtnRef?.setHasText;
- unpatches.push(() => { if (sendBtnRef && origSetHasText) sendBtnRef.setHasText = origSetHasText; });
- if (sendBtnRef) {
- sendBtnRef.setHasText = (hasText_: boolean) => {
- hasText = hasText_;
- if (origSetHasText) return origSetHasText.call(sendBtnRef, hasText_);
- };
- }
- }
- });
- });
- })
- );
- },
- onUnload() {
- for (const unpatch of unpatches) unpatch?.();
- unpatches.length = 0;
- },
- settings: ChatboxAvatarSettings,
-};
diff --git a/plugins/ChatboxAvatar/src/settings.tsx b/plugins/ChatboxAvatar/src/settings.tsx
deleted file mode 100644
index e4005eb..0000000
--- a/plugins/ChatboxAvatar/src/settings.tsx
+++ /dev/null
@@ -1,101 +0,0 @@
-import { plugin } from "@vendetta";
-import { useProxy } from "@vendetta/storage";
-import { React, ReactNative } from "@vendetta/metro/common";
-import { findByProps } from "@vendetta/metro";
-
-const { ScrollView } = ReactNative;
-const { TableRowGroup, TableCheckboxRow, Stack } = findByProps(
- "TableCheckboxRow", "TableRowGroup", "Stack"
-);
-
-plugin.storage.pressAction ??= "profile";
-plugin.storage.longPressAction ??= "server";
-plugin.storage.showStatusCutout ??= false;
-plugin.storage.collapseWhileTyping ??= false;
-
-export default function ChatboxAvatarSettings() {
- useProxy(plugin.storage);
- const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
-
- return (
-
-
-
- {
- plugin.storage.pressAction = "profile";
- forceUpdate();
- }}
- />
- {
- plugin.storage.pressAction = "server";
- forceUpdate();
- }}
- />
-
-
-
- {
- plugin.storage.longPressAction = "profile";
- forceUpdate();
- }}
- />
- {
- plugin.storage.longPressAction = "server";
- forceUpdate();
- }}
- />
-
-
-
- {
- plugin.storage.showStatusCutout = true;
- forceUpdate();
- }}
- />
- {
- plugin.storage.showStatusCutout = false;
- forceUpdate();
- }}
- />
-
-
-
- {
- plugin.storage.collapseWhileTyping = true;
- forceUpdate();
- }}
- />
- {
- plugin.storage.collapseWhileTyping = false;
- forceUpdate();
- }}
- />
-
-
-
- );
-}
diff --git a/plugins/multi-scrobbler/src/constants.ts b/plugins/multi-scrobbler/src/constants.ts
index 8d627c1..3173733 100644
--- a/plugins/multi-scrobbler/src/constants.ts
+++ b/plugins/multi-scrobbler/src/constants.ts
@@ -7,6 +7,8 @@ const Constants = {
APPLICATION_ID: "1368513179272871956",
// Don't check more than once every 3 seconds to avoid getting rate limited
MIN_UPDATE_INTERVAL: 3,
+ // Libre.fm specifically requested a 1-minute minimum to reduce load
+ LIBREFM_MIN_UPDATE_INTERVAL: 60,
// How many times to retry failed API calls
MAX_RETRY_ATTEMPTS: 3,
// Wait 5 seconds between retries
diff --git a/plugins/multi-scrobbler/src/manager.ts b/plugins/multi-scrobbler/src/manager.ts
index e5a67d6..2cd0a98 100644
--- a/plugins/multi-scrobbler/src/manager.ts
+++ b/plugins/multi-scrobbler/src/manager.ts
@@ -381,10 +381,18 @@ class PluginManager {
// Don't reset track state - let updateActivity() handle whether to update
await this.updateActivity();
+ const currentService = currentSettings.service;
+ let minInterval: number = Constants.MIN_UPDATE_INTERVAL;
+
+ if (currentService === "librefm") {
+ minInterval = Constants.LIBREFM_MIN_UPDATE_INTERVAL;
+ log(`Libre.fm active: enforcing ${minInterval}s minimum update interval`);
+ }
+
const interval = Math.max(
(Number(currentSettings.timeInterval) ||
- Constants.DEFAULT_SETTINGS.timeInterval) * 1000,
- Constants.MIN_UPDATE_INTERVAL * 1000,
+ Constants.DEFAULT_SETTINGS.timeInterval) * 1000,
+ minInterval * 1000,
);
this.updateTimer = setInterval(() => this.updateActivity(), interval);
diff --git a/plugins/multi-scrobbler/src/ui/pages/pages/DisplaySettingsPage.tsx b/plugins/multi-scrobbler/src/ui/pages/pages/DisplaySettingsPage.tsx
index 25bd4a9..46cf837 100644
--- a/plugins/multi-scrobbler/src/ui/pages/pages/DisplaySettingsPage.tsx
+++ b/plugins/multi-scrobbler/src/ui/pages/pages/DisplaySettingsPage.tsx
@@ -15,6 +15,9 @@ export default function DisplaySettingsPage() {
useProxy(plugin.storage);
const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
+ const currentService = getStorage("service");
+ const isLibreFm = currentService === "librefm";
+
return (
@@ -29,24 +32,33 @@ export default function DisplaySettingsPage() {
}}
isClearable
/>
- {
- const interval = Number(v);
- if (interval >= Constants.MIN_UPDATE_INTERVAL) {
- setStorage("timeInterval", interval);
- forceUpdate();
- }
- }}
- keyboardType="numeric"
- isClearable
- />
+ {isLibreFm ? (
+
+ ) : (
+ {
+ const interval = Number(v);
+ if (interval >= Constants.MIN_UPDATE_INTERVAL) {
+ setStorage("timeInterval", interval);
+ forceUpdate();
+ }
+ }}
+ keyboardType="numeric"
+ isClearable
+ />
+ )}