Skip to content

Commit

Permalink
Implement partial support for Array Settings (#3152)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nuckyz authored Jan 19, 2025
1 parent a60af65 commit b963171
Show file tree
Hide file tree
Showing 11 changed files with 303 additions and 238 deletions.
4 changes: 3 additions & 1 deletion src/components/PluginSettings/PluginModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ const Components: Record<OptionType, React.ComponentType<ISettingElementProps<an
[OptionType.BOOLEAN]: SettingBooleanComponent,
[OptionType.SELECT]: SettingSelectComponent,
[OptionType.SLIDER]: SettingSliderComponent,
[OptionType.COMPONENT]: SettingCustomComponent
[OptionType.COMPONENT]: SettingCustomComponent,
// TODO: Add UI for Array settings
[OptionType.ARRAY]: () => null,
};

export default function PluginModal({ plugin, onRestartNeeded, onClose, transitionState }: PluginModalProps) {
Expand Down
52 changes: 21 additions & 31 deletions src/plugins/ignoreActivities/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/

import * as DataStore from "@api/DataStore";
import { definePluginSettings, Settings } from "@api/Settings";
import { getUserSettingLazy } from "@api/UserSettings";
import ErrorBoundary from "@components/ErrorBoundary";
Expand Down Expand Up @@ -71,9 +70,9 @@ function ToggleActivityComponent(activity: IgnoredActivity, isPlaying = false) {
function handleActivityToggle(e: React.MouseEvent<HTMLButtonElement, 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);
const ignoredActivityIndex = settings.store.ignoredActivities.findIndex(act => act.id === activity.id);
if (ignoredActivityIndex === -1) settings.store.ignoredActivities.push(activity);
else settings.store.ignoredActivities.splice(ignoredActivityIndex, 1);

recalculateActivities();
}
Expand Down Expand Up @@ -209,14 +208,14 @@ const settings = definePluginSettings({
description: "Ignore all competing activities (These are normally special game activities)",
default: false,
onChange: recalculateActivities
},
ignoredActivities: {
type: OptionType.ARRAY,
description: "",
hidden: true,
default: [] as IgnoredActivity[]
}
}).withPrivateSettings<{
ignoredActivities: IgnoredActivity[];
}>();

function getIgnoredActivities() {
return settings.store.ignoredActivities ??= [];
}
});

function isActivityTypeIgnored(type: number, id?: string) {
if (id && settings.store.idsList.includes(id)) {
Expand Down Expand Up @@ -284,43 +283,34 @@ export default definePlugin({
],

async start() {
// Migrate allowedIds
if (Settings.plugins.IgnoreActivities.allowedIds) {
settings.store.idsList = Settings.plugins.IgnoreActivities.allowedIds;
delete Settings.plugins.IgnoreActivities.allowedIds; // Remove allowedIds
}

const oldIgnoredActivitiesData = await DataStore.get<Map<IgnoredActivity["id"], IgnoredActivity>>("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) {
if (settings.store.ignoredActivities.length !== 0) {
const gamesSeen = RunningGameStore.getGamesSeen() as { id?: string; exePath: string; }[];

for (const [index, ignoredActivity] of getIgnoredActivities().entries()) {
let hasChanges = false;
for (const [index, ignoredActivity] of settings.store.ignoredActivities.entries()) {
if (ignoredActivity.type !== ActivitiesTypes.Game) continue;

if (!gamesSeen.some(game => game.id === ignoredActivity.id || game.exePath === ignoredActivity.id)) {
getIgnoredActivities().splice(index, 1);
settings.store.ignoredActivities.splice(index, 1);
hasChanges = true;
}
}

if (hasChanges) {
recalculateActivities();
}
}
},

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.listMode === FilterMode.Whitelist && settings.store.idsList.includes(props.application_id));
return !settings.store.ignoredActivities.some(activity => activity.id === props.application_id) || (settings.store.listMode === FilterMode.Whitelist && settings.store.idsList.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 !settings.store.ignoredActivities.some(activity => activity.id === exePath);
}
}

Expand Down
7 changes: 6 additions & 1 deletion src/plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { addContextMenuPatch, removeContextMenuPatch } from "@api/ContextMenu";
import { Settings } from "@api/Settings";
import { Logger } from "@utils/Logger";
import { canonicalizeFind } from "@utils/patches";
import { Patch, Plugin, ReporterTestable, StartAt } from "@utils/types";
import { OptionType, Patch, Plugin, ReporterTestable, StartAt } from "@utils/types";
import { FluxDispatcher } from "@webpack/common";
import { FluxEvents } from "@webpack/types";

Expand Down Expand Up @@ -119,6 +119,11 @@ for (const p of pluginsValues) {
for (const [name, def] of Object.entries(p.settings.def)) {
const checks = p.settings.checks?.[name];
p.options[name] = { ...def, ...checks };

// TODO: Remove this once Array settings have an UI implementation
if (p.options[name].type === OptionType.ARRAY) {
p.options[name].hidden = true;
}
}
}

Expand Down
70 changes: 47 additions & 23 deletions src/plugins/messageTags/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, registerCommand, sendBotMessage, unregisterCommand } from "@api/Commands";
import * as DataStore from "@api/DataStore";
import { Settings } from "@api/Settings";
import { definePluginSettings, Settings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";

Expand All @@ -32,28 +32,29 @@ interface Tag {
enabled: boolean;
}

const getTags = () => DataStore.get(DATA_KEY).then<Tag[]>(t => t ?? []);
const getTag = (name: string) => DataStore.get(DATA_KEY).then<Tag | null>((t: Tag[]) => (t ?? []).find((tt: Tag) => tt.name === name) ?? null);
const addTag = async (tag: Tag) => {
const tags = await getTags();
tags.push(tag);
DataStore.set(DATA_KEY, tags);
return tags;
};
const removeTag = async (name: string) => {
let tags = await getTags();
tags = await tags.filter((t: Tag) => t.name !== name);
DataStore.set(DATA_KEY, tags);
return tags;
};
function getTags() {
return settings.store.tagsList;
}

function getTag(name: string) {
return settings.store.tagsList.find(tag => tag.name === name) ?? null;
}

function addTag(tag: Tag) {
settings.store.tagsList.push(tag);
}

function removeTag(name: string) {
settings.store.tagsList = settings.store.tagsList.filter(tag => tag.name !== name);
}

function createTagCommand(tag: Tag) {
registerCommand({
name: tag.name,
description: tag.name,
inputType: ApplicationCommandInputType.BUILT_IN_TEXT,
execute: async (_, ctx) => {
if (!await getTag(tag.name)) {
if (!getTag(tag.name)) {
sendBotMessage(ctx.channel.id, {
content: `${EMOTE} The tag **${tag.name}** does not exist anymore! Please reload ur Discord to fix :)`
});
Expand All @@ -69,6 +70,20 @@ function createTagCommand(tag: Tag) {
}, "CustomTags");
}

const settings = definePluginSettings({
clyde: {
name: "Clyde message on send",
description: "If enabled, clyde will send you an ephemeral message when a tag was used.",
type: OptionType.BOOLEAN,
default: true
},
tagsList: {
type: OptionType.ARRAY,
description: "",
hidden: true,
default: [] as Tag[],
}
});

export default definePlugin({
name: "MessageTags",
Expand All @@ -84,7 +99,16 @@ export default definePlugin({
},

async start() {
for (const tag of await getTags()) createTagCommand(tag);
// TODO: Remove DataStore tags migration once enough time has passed
const oldTags = await DataStore.get<Tag[]>(DATA_KEY);
if (oldTags != null) {
settings.store.tagsList = oldTags;
await DataStore.del(DATA_KEY);
}

for (const tag of getTags()) {
createTagCommand(tag);
}
},

commands: [
Expand Down Expand Up @@ -153,7 +177,7 @@ export default definePlugin({
const name: string = findOption(args[0].options, "tag-name", "");
const message: string = findOption(args[0].options, "message", "");

if (await getTag(name))
if (getTag(name))
return sendBotMessage(ctx.channel.id, {
content: `${EMOTE} A Tag with the name **${name}** already exists!`
});
Expand All @@ -165,7 +189,7 @@ export default definePlugin({
};

createTagCommand(tag);
await addTag(tag);
addTag(tag);

sendBotMessage(ctx.channel.id, {
content: `${EMOTE} Successfully created the tag **${name}**!`
Expand All @@ -175,13 +199,13 @@ export default definePlugin({
case "delete": {
const name: string = findOption(args[0].options, "tag-name", "");

if (!await getTag(name))
if (!getTag(name))
return sendBotMessage(ctx.channel.id, {
content: `${EMOTE} A Tag with the name **${name}** does not exist!`
});

unregisterCommand(name);
await removeTag(name);
removeTag(name);

sendBotMessage(ctx.channel.id, {
content: `${EMOTE} Successfully deleted the tag **${name}**!`
Expand All @@ -195,7 +219,7 @@ export default definePlugin({
// @ts-ignore
title: "All Tags:",
// @ts-ignore
description: (await getTags())
description: getTags()
.map(tag => `\`${tag.name}\`: ${tag.message.slice(0, 72).replaceAll("\\n", " ")}${tag.message.length > 72 ? "..." : ""}`)
.join("\n") || `${EMOTE} Woops! There are no tags yet, use \`/tags create\` to create one!`,
// @ts-ignore
Expand All @@ -208,7 +232,7 @@ export default definePlugin({
}
case "preview": {
const name: string = findOption(args[0].options, "tag-name", "");
const tag = await getTag(name);
const tag = getTag(name);

if (!tag)
return sendBotMessage(ctx.channel.id, {
Expand Down
12 changes: 5 additions & 7 deletions src/plugins/pinDms/components/CreateCategoryModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import { extractAndLoadChunksLazy, findComponentByCodeLazy, findExportedComponen
import { Button, Forms, Text, TextInput, Toasts, useEffect, useState } from "@webpack/common";

import { DEFAULT_COLOR, SWATCHES } from "../constants";
import { categories, Category, createCategory, getCategory, updateCategory } from "../data";
import { forceUpdate } from "../index";
import { Category, categoryLen, createCategory, getCategory, updateCategory } from "../data";

interface ColorPickerProps {
color: number | null;
Expand Down Expand Up @@ -52,7 +51,7 @@ function useCategory(categoryId: string | null, initalChannelId: string | null)
else if (initalChannelId)
setCategory({
id: Toasts.genId(),
name: `Pin Category ${categories.length + 1}`,
name: `Pin Category ${categoryLen() + 1}`,
color: DEFAULT_COLOR,
collapsed: false,
channels: [initalChannelId]
Expand All @@ -70,14 +69,13 @@ export function NewCategoryModal({ categoryId, modalProps, initalChannelId }: Pr

if (!category) return null;

const onSave = async (e: React.FormEvent<HTMLFormElement> | React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
const onSave = (e: React.FormEvent<HTMLFormElement> | React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
e.preventDefault();
if (!categoryId)
await createCategory(category);
createCategory(category);
else
await updateCategory(category);
updateCategory(category);

forceUpdate();
modalProps.onClose();
};

Expand Down
14 changes: 7 additions & 7 deletions src/plugins/pinDms/components/contextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
import { Menu } from "@webpack/common";

import { addChannelToCategory, canMoveChannelInDirection, categories, isPinned, moveChannel, removeChannelFromCategory } from "../data";
import { forceUpdate, PinOrder, settings } from "../index";
import { addChannelToCategory, canMoveChannelInDirection, currentUserCategories, isPinned, moveChannel, removeChannelFromCategory } from "../data";
import { PinOrder, settings } from "../index";
import { openCategoryModal } from "./CreateCategoryModal";

function createPinMenuItem(channelId: string) {
Expand All @@ -31,12 +31,12 @@ function createPinMenuItem(channelId: string) {
<Menu.MenuSeparator />

{
categories.map(category => (
currentUserCategories.map(category => (
<Menu.MenuItem
key={category.id}
id={`pin-category-${category.id}`}
label={category.name}
action={() => addChannelToCategory(channelId, category.id).then(forceUpdate)}
action={() => addChannelToCategory(channelId, category.id)}
/>
))
}
Expand All @@ -49,15 +49,15 @@ function createPinMenuItem(channelId: string) {
id="unpin-dm"
label="Unpin DM"
color="danger"
action={() => removeChannelFromCategory(channelId).then(forceUpdate)}
action={() => removeChannelFromCategory(channelId)}
/>

{
settings.store.pinOrder === PinOrder.Custom && canMoveChannelInDirection(channelId, -1) && (
<Menu.MenuItem
id="move-up"
label="Move Up"
action={() => moveChannel(channelId, -1).then(forceUpdate)}
action={() => moveChannel(channelId, -1)}
/>
)
}
Expand All @@ -67,7 +67,7 @@ function createPinMenuItem(channelId: string) {
<Menu.MenuItem
id="move-down"
label="Move Down"
action={() => moveChannel(channelId, 1).then(forceUpdate)}
action={() => moveChannel(channelId, 1)}
/>
)
}
Expand Down
Loading

0 comments on commit b963171

Please sign in to comment.