/* * Vencord, a Discord client mod * Copyright (c) 2023 Vendicated and contributors * SPDX-License-Identifier: GPL-3.0-or-later */ import * as DataStore from "@api/DataStore"; import { definePluginSettings, Settings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Flex } from "@components/Flex"; import { Devs } from "@utils/constants"; import { Margins } from "@utils/margins"; import definePlugin, { OptionType } from "@utils/types"; import { findStoreLazy } from "@webpack"; import { Button, Forms, showToast, StatusSettingsStores, TextInput, Toasts, Tooltip, useEffect, useState } from "webpack/common"; const enum ActivitiesTypes { Game, Embedded } interface IgnoredActivity { id: string; name: string; type: ActivitiesTypes; } const RunningGameStore = findStoreLazy("RunningGameStore"); function ToggleIcon(activity: IgnoredActivity, tooltipText: string, path: string, fill: string) { return ( {tooltipProps => ( )} ); } const ToggleIconOn = (activity: IgnoredActivity, fill: string) => ToggleIcon(activity, "Disable Activity", "M480-320q75 0 127.5-52.5T660-500q0-75-52.5-127.5T480-680q-75 0-127.5 52.5T300-500q0 75 52.5 127.5T480-320Zm0-72q-45 0-76.5-31.5T372-500q0-45 31.5-76.5T480-608q45 0 76.5 31.5T588-500q0 45-31.5 76.5T480-392Zm0 192q-146 0-266-81.5T40-500q54-137 174-218.5T480-800q146 0 266 81.5T920-500q-54 137-174 218.5T480-200Zm0-300Zm0 220q113 0 207.5-59.5T832-500q-50-101-144.5-160.5T480-720q-113 0-207.5 59.5T128-500q50 101 144.5 160.5T480-280Z", fill); const ToggleIconOff = (activity: IgnoredActivity, fill: string) => ToggleIcon(activity, "Enable Activity", "m644-428-58-58q9-47-27-88t-93-32l-58-58q17-8 34.5-12t37.5-4q75 0 127.5 52.5T660-500q0 20-4 37.5T644-428Zm128 126-58-56q38-29 67.5-63.5T832-500q-50-101-143.5-160.5T480-720q-29 0-57 4t-55 12l-62-62q41-17 84-25.5t90-8.5q151 0 269 83.5T920-500q-23 59-60.5 109.5T772-302Zm20 246L624-222q-35 11-70.5 16.5T480-200q-151 0-269-83.5T40-500q21-53 53-98.5t73-81.5L56-792l56-56 736 736-56 56ZM222-624q-29 26-53 57t-41 67q50 101 143.5 160.5T480-280q20 0 39-2.5t39-5.5l-36-38q-11 3-21 4.5t-21 1.5q-75 0-127.5-52.5T300-500q0-11 1.5-21t4.5-21l-84-82Zm319 93Zm-151 75Z", fill); function ToggleActivityComponent(activity: IgnoredActivity, isPlaying = false) { const s = settings.use(["ignoredActivities"]); const { ignoredActivities = [] } = s; if (ignoredActivities.some(act => act.id === activity.id)) return ToggleIconOff(activity, "var(--status-danger)"); return ToggleIconOn(activity, isPlaying ? "var(--green-300)" : "var(--primary-400)"); } function handleActivityToggle(e: React.MouseEvent, activity: IgnoredActivity) { e.stopPropagation(); const ignoredActivityIndex = getIgnoredActivities().findIndex(act => act.id === activity.id); if (ignoredActivityIndex === -1) settings.store.ignoredActivities = getIgnoredActivities().concat(activity); else settings.store.ignoredActivities = getIgnoredActivities().filter((_, index) => index !== ignoredActivityIndex); // Trigger activities recalculation StatusSettingsStores.ShowCurrentGame.updateSetting(old => old); } function ImportCustomRPCComponent() { return ( Import the application id of the CustomRPC plugin to the allowed list
); } let allowedIdsPushID: ((id: string) => boolean) | null = null; function AllowedIdsComponent(props: { setValue: (value: string) => void; }) { const [allowedIds, setAllowedIds] = useState(settings.store.allowedIds ?? ""); allowedIdsPushID = (id: string) => { const currentIds = new Set(allowedIds.split(",").map(id => id.trim()).filter(Boolean)); const isAlreadyAdded = currentIds.has(id) || (currentIds.add(id), false); const ids = Array.from(currentIds).join(", "); setAllowedIds(ids); props.setValue(ids); return isAlreadyAdded; }; useEffect(() => () => { allowedIdsPushID = null; }, []); function handleChange(newValue: string) { setAllowedIds(newValue); props.setValue(newValue); } return ( Allowed List Comma separated list of activity IDs to allow (Useful for allowing RPC activities and CustomRPC) ); } const settings = definePluginSettings({ importCustomRPC: { type: OptionType.COMPONENT, description: "", component: () => }, allowedIds: { type: OptionType.COMPONENT, description: "", default: "", onChange(newValue: string) { const ids = new Set(newValue.split(",").map(id => id.trim()).filter(Boolean)); settings.store.allowedIds = Array.from(ids).join(", "); }, component: props => }, ignorePlaying: { type: OptionType.BOOLEAN, description: "Ignore all playing activities (These are usually game and RPC activities)", default: false }, ignoreStreaming: { type: OptionType.BOOLEAN, description: "Ignore all streaming activities", default: false }, ignoreListening: { type: OptionType.BOOLEAN, description: "Ignore all listening activities (These are usually spotify activities)", default: false }, ignoreWatching: { type: OptionType.BOOLEAN, description: "Ignore all watching activities", default: false }, ignoreCompeting: { type: OptionType.BOOLEAN, description: "Ignore all competing activities (These are normally special game activities)", default: false } }).withPrivateSettings<{ ignoredActivities: IgnoredActivity[]; }>(); function getIgnoredActivities() { return settings.store.ignoredActivities ??= []; } function isActivityTypeIgnored(type: number, id?: string) { if (id && settings.store.allowedIds.includes(id)) { return false; } switch (type) { case 0: return settings.store.ignorePlaying; case 1: return settings.store.ignoreStreaming; case 2: return settings.store.ignoreListening; case 3: return settings.store.ignoreWatching; case 5: return settings.store.ignoreCompeting; } return false; } export default definePlugin({ name: "IgnoreActivities", authors: [Devs.Nuckyz], description: "Ignore activities from showing up on your status ONLY. You can configure which ones are specifically ignored from the Registered Games and Activities tabs, or use the general settings below.", settings, patches: [ { find: '="LocalActivityStore",', replacement: [ { match: /HANG_STATUS.+?(?=!\i\(\)\(\i,\i\)&&)(?<=(\i)\.push.+?)/, replace: (m, activities) => `${m}${activities}=${activities}.filter($self.isActivityNotIgnored);` } ] }, { find: ".Messages.SETTINGS_GAMES_TOGGLE_OVERLAY", replacement: { match: /\.Messages\.SETTINGS_GAMES_TOGGLE_OVERLAY.+?}\(\),(?<={overlay:\i,.+?=(\i),.+?)(?=!(\i))/, replace: (m, props, nowPlaying) => `${m}$self.renderToggleGameActivityButton(${props},${nowPlaying}),` } }, { find: ".activityTitleText,variant", replacement: { match: /(?<=\i\.activityTitleText.+?children:(\i)\.name.*?}\),)/, replace: (_, props) => `$self.renderToggleActivityButton(${props}),` }, }, { find: ".activityCardDetails,children", replacement: { match: /(?<=\i\.activityCardDetails.+?children:(\i\.application)\.name.*?}\),)/, replace: (_, props) => `$self.renderToggleActivityButton(${props}),` } } ], async start() { const oldIgnoredActivitiesData = await DataStore.get>("IgnoreActivities_ignoredActivities"); if (oldIgnoredActivitiesData != null) { settings.store.ignoredActivities = Array.from(oldIgnoredActivitiesData.values()) .map(activity => ({ ...activity, name: "Unknown Name" })); DataStore.del("IgnoreActivities_ignoredActivities"); } if (getIgnoredActivities().length !== 0) { const gamesSeen = RunningGameStore.getGamesSeen() as { id?: string; exePath: string; }[]; for (const [index, ignoredActivity] of getIgnoredActivities().entries()) { if (ignoredActivity.type !== ActivitiesTypes.Game) continue; if (!gamesSeen.some(game => game.id === ignoredActivity.id || game.exePath === ignoredActivity.id)) { getIgnoredActivities().splice(index, 1); } } } }, isActivityNotIgnored(props: { type: number; application_id?: string; name?: string; }) { if (isActivityTypeIgnored(props.type, props.application_id)) return false; if (props.application_id != null) { return !getIgnoredActivities().some(activity => activity.id === props.application_id) || settings.store.allowedIds.includes(props.application_id); } else { const exePath = RunningGameStore.getRunningGames().find(game => game.name === props.name)?.exePath; if (exePath) { return !getIgnoredActivities().some(activity => activity.id === exePath); } } return true; }, renderToggleGameActivityButton(props: { id?: string; name: string, exePath: string; }, nowPlaying: boolean) { return (
{ToggleActivityComponent({ id: props.id ?? props.exePath, name: props.name, type: ActivitiesTypes.Game }, nowPlaying)}
); }, renderToggleActivityButton(props: { id: string; name: string; }) { return ( {ToggleActivityComponent({ id: props.id, name: props.name, type: ActivitiesTypes.Embedded })} ); } });