add wallpaperFree (#262)

* add wallpaperFree

* fix lint ????????

* fix lint again

* Fixes

---------

Co-authored-by: thororen1234 <78185467+thororen1234@users.noreply.github.com>
This commit is contained in:
Creation's 2025-05-16 18:18:07 -04:00 committed by GitHub
parent 1c24c4d173
commit 78209ef7bf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 453 additions and 1 deletions

View file

@ -98,7 +98,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch
- JumpToStart by Samwich
- KeyboardSounds by HypedDomi
- KeywordNotify by camila314 & x3rt
- - LastActive by Crxa
- LastActive by Crxa
- LimitMiddleClickPaste by no dev listed
- LoginWithQR by nexpid
- MediaPlaybackSpeed by D3SOX
@ -177,6 +177,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch
- ViewRawVariant by Kyuuhachi
- VoiceChatUtilities by D3SOX
- VoiceJoinMessages by Sqaaakoi & maintained by thororen
- WallpaperFree by Joona
- WebpackTarball by Kyuuhachi
- WhitelistedEmojis by Creations
- WhosWatching by fres

View file

@ -0,0 +1,67 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2025 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { NavContextMenuPatchCallback } from "@api/ContextMenu";
import { openModal } from "@utils/modal";
import { ChannelStore, FluxDispatcher, Menu } from "@webpack/common";
import { SetCustomWallpaperModal, SetDiscordWallpaperModal } from "./modal";
import { ChatWallpaperStore, fetchWallpapers } from "./util";
const addWallpaperMenu = (channelId?: string, guildId?: string) => {
const setWallpaper = (url?: string) => {
FluxDispatcher.dispatch({
// @ts-ignore
type: "VC_WALLPAPER_FREE_CHANGE",
channelId,
guildId,
url,
});
};
return (
<Menu.MenuItem label="Wallpaper Free" key="vc-wpfree-menu" id="vc-wpfree-menu">
<Menu.MenuItem
label="Set custom wallpaper"
id="vc-wpfree-set-custom"
action={() => openModal(props => <SetCustomWallpaperModal props={props} onSelect={setWallpaper} />)}
/>
<Menu.MenuItem
label="Set a Discord wallpaper"
id="vc-wpfree-set-discord"
action={async () => {
ChatWallpaperStore.shouldFetchWallpapers && await fetchWallpapers();
openModal(props => <SetDiscordWallpaperModal props={props} onSelect={setWallpaper} />);
}}
/>
<Menu.MenuSeparator />
<Menu.MenuItem
label="Remove Custom Wallpaper"
id="vc-wpfree-remove"
color="danger"
action={() => setWallpaper(void 0)}
/>
</Menu.MenuItem>
);
};
const UserContextPatch: NavContextMenuPatchCallback = (children, args) => {
if (!args.user) return;
const dmChannelId = ChannelStore.getDMFromUserId(args.user.id);
children.push(addWallpaperMenu(dmChannelId));
};
const ChannelContextPatch: NavContextMenuPatchCallback = (children, args) => {
if (!args.channel) return;
children.push(addWallpaperMenu(args.channel.id));
};
const GuildContextPatch: NavContextMenuPatchCallback = (children, args) => {
if (!args.guild) return;
children.push(addWallpaperMenu(void 0, args.guild.id));
};
export { ChannelContextPatch, GuildContextPatch, UserContextPatch };

View file

@ -0,0 +1,116 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2025 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal";
import { Button, lodash, Text, TextInput, useState, useStateFromStores } from "@webpack/common";
import { ChatWallpaperStore, Wallpaper } from "./util";
interface Props {
props: ModalProps;
onSelect: (url: string) => void;
}
export function SetCustomWallpaperModal({ props, onSelect }: Props) {
const [url, setUrl] = useState("");
return (
<ModalRoot {...props} size={ModalSize.SMALL}>
<ModalHeader>
<Text variant="heading-lg/normal" style={{ marginBottom: 8 }}>
Set a custom wallpaper
</Text>
</ModalHeader>
<ModalContent>
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
<TextInput
placeholder="The image url"
value={url}
onChange={setUrl}
autoFocus
/>
{url && (
<img
src={url}
alt="Wallpaper preview"
style={{
display: "block",
width: "100%",
height: "auto",
objectFit: "cover",
border: "1px solid var(--background-modifier-accent)",
borderRadius: 8
}}
/>
)}
<div style={{ display: "flex", justifyContent: "flex-end", gap: 8 }}>
<Button onClick={props.onClose}>Cancel</Button>
<Button
color={Button.Colors.BRAND}
onClick={() => {
onSelect(url);
props.onClose();
}}
disabled={!url}
>Apply</Button>
</div>
</div>
</ModalContent>
</ModalRoot>
);
}
export function SetDiscordWallpaperModal({ props, onSelect }: Props) {
const discordWallpapers: Wallpaper[] = useStateFromStores([ChatWallpaperStore], () => ChatWallpaperStore.wallpapers);
return (
<ModalRoot {...props} size={ModalSize.MEDIUM}>
<ModalHeader>
<Text variant="heading-lg/normal" style={{ marginBottom: 8 }}>
Choose a Discord Wallpaper
</Text>
</ModalHeader>
<ModalContent>
<div className="vc-wpfree-discord-wp-modal">
{lodash.chunk(discordWallpapers, 2).map(group => {
const main = group[0];
return (
<div key={main.id} className="vc-wpfree-discord-wp-icon-container">
<figure style={{ margin: 0, textAlign: "center" }}>
<img
className="vc-wpfree-discord-wp-icon-img"
src={`https://cdn.discordapp.com/assets/content/${main.default.icon}`}
alt={main.label}
/>
<figcaption>
<Text variant="text-md/normal">{main.label}</Text>
</figcaption>
</figure>
<div className="vc-wpfree-discord-set-buttons">
{group.map(wp => (
<Button
key={wp.id}
size={Button.Sizes.SMALL}
color={Button.Colors.BRAND}
onClick={() => {
onSelect(`https://cdn.discordapp.com/assets/content/${wp.default.asset}`);
props.onClose();
}}
>
{wp.isBlurred ? "Blurred" : "Normal"}
</Button>
))}
</div>
</div>
);
})}
</div>
</ModalContent>
</ModalRoot>
);
}

View file

@ -0,0 +1,70 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2025 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { openModal } from "@utils/modal";
import { findByCodeLazy, findStoreLazy } from "@webpack";
import { Button, FluxDispatcher } from "@webpack/common";
import { SetCustomWallpaperModal, SetDiscordWallpaperModal } from "./modal";
export const ChatWallpaperStore = findStoreLazy("ChatWallpaperStore");
export const fetchWallpapers = findByCodeLazy('type:"FETCH_CHAT_WALLPAPERS_SUCCESS"');
export function GlobalDefaultComponent() {
const setGlobal = (url?: string) => {
FluxDispatcher.dispatch({
// @ts-ignore
type: "VC_WALLPAPER_FREE_CHANGE_GLOBAL",
url,
});
};
return (
<>
<Button onClick={() => {
openModal(props => <SetCustomWallpaperModal props={props} onSelect={setGlobal} />);
}}>Set a global custom wallpaper</Button>
<Button onClick={async () => {
ChatWallpaperStore.shouldFetchWallpapers && await fetchWallpapers();
openModal(props => <SetDiscordWallpaperModal props={props} onSelect={setGlobal} />);
}}>Set a global Discord wallpaper</Button>
<Button
color={Button.Colors.RED}
onClick={() => setGlobal(void 0)}
>Remove global default wallpaper</Button>
<Button
color={Button.Colors.RED}
onClick={() => {
// @ts-ignore
FluxDispatcher.dispatch({ type: "VC_WALLPAPER_FREE_RESET" });
}}
>Reset wallpaper data</Button>
</>
);
}
export interface Wallpaper {
id: string;
label: string;
default: Default;
variants: Variants;
isBlurred: boolean;
designGroupId: string;
}
export interface Default {
asset: string;
icon: string;
thumbhash: string;
opacity?: number;
}
export interface Variants {
dark: Default;
}

View file

@ -0,0 +1,86 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2023 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import "./styles.css";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { useStateFromStores } from "@webpack/common";
import { Channel } from "discord-types/general";
import { ChannelContextPatch, GuildContextPatch, UserContextPatch } from "./components/ctxmenu";
import { GlobalDefaultComponent, Wallpaper } from "./components/util";
import { WallpaperFreeStore } from "./store";
const settings = definePluginSettings({
forceReplace: {
description: "If a dm wallpaper is already set, your custom wallpaper will be used instead.",
type: OptionType.BOOLEAN,
default: false,
},
globalDefault: {
description: "Set a global default wallpaper for all channels.",
type: OptionType.COMPONENT,
component: GlobalDefaultComponent
}
});
export default definePlugin({
name: "WallpaperFree",
authors: [Devs.Joona],
description: "Use the DM wallpapers anywhere or set a custom wallpaper",
patches: [
{
find: ".wallpaperContainer,",
group: true,
replacement: [
{
match: /return null==(\i).+?\?null:/,
replace: "const vcWpFreeCustom = $self.customWallpaper(arguments[0].channel,$1);return !($1||vcWpFreeCustom)?null:"
},
{
match: /,{chatWallpaperState:/,
replace: "$&vcWpFreeCustom||"
},
{
match: /(\i=)(.{1,50}""\))/,
replace: "$1arguments[0].chatWallpaperState.vcWallpaperUrl||$2"
},
{
match: /(\i\.isViewable&&)(null!=\i)/,
replace: "$1($2||arguments[0].chatWallpaperState.vcWallpaperUrl)"
},
]
}
],
settings,
contextMenus: {
"user-context": UserContextPatch,
"channel-context": ChannelContextPatch,
"thread-context": ChannelContextPatch,
"guild-context": GuildContextPatch,
"gdm-context": ChannelContextPatch,
},
customWallpaper(channel: Channel, wp: Wallpaper | undefined) {
const { forceReplace } = settings.use(["forceReplace"]);
const url = useStateFromStores([WallpaperFreeStore], () => WallpaperFreeStore.getUrl(channel));
if (!forceReplace && wp?.id)
return wp;
if (url) {
return {
wallpaperId: "id",
vcWallpaperUrl: url,
isViewable: true,
};
}
return void 0;
},
});

View file

@ -0,0 +1,82 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { proxyLazy } from "@utils/lazy";
import { findByPropsLazy } from "@webpack";
import { FluxDispatcher } from "@webpack/common";
import { FluxEmitter, FluxStore } from "@webpack/types";
import { Channel } from "discord-types/general";
interface IFlux {
PersistedStore: typeof FluxStore;
Emitter: FluxEmitter;
}
const Flux: IFlux = findByPropsLazy("connectStores");
export const WallpaperFreeStore = proxyLazy(() => {
const wallpaperChannelMap: Map<string, string> = new Map();
const wallpaperGuildMap: Map<string, string> = new Map();
let globalDefault: string | undefined;
class WallpaperFreeStore extends Flux.PersistedStore {
static persistKey = "WallpaperFreeStore";
// @ts-ignore
initialize(previous: { guildMap: Map<string, string>, channelMap: Map<string, string>, globalDefault: string; } | undefined) {
if (!previous)
return;
wallpaperGuildMap.clear();
wallpaperChannelMap.clear();
for (const [channel, url] of previous.channelMap) {
wallpaperChannelMap.set(channel, url);
}
for (const [guild, url] of previous.guildMap) {
wallpaperGuildMap.set(guild, url);
}
globalDefault = previous.globalDefault;
}
getState() {
return { guildMap: Array.from(wallpaperGuildMap), channelMap: Array.from(wallpaperChannelMap), globalDefault };
}
getUrl(channel: Channel): string | undefined {
return (
wallpaperChannelMap.get(channel.id) ??
wallpaperGuildMap.get(channel.guild_id) ??
globalDefault
);
}
}
const store = new WallpaperFreeStore(FluxDispatcher, {
// @ts-ignore
VC_WALLPAPER_FREE_CHANGE({ guildId, channelId, url }: { guildId: string | undefined, channelId: string | undefined, url: string; }) {
if (guildId) {
wallpaperGuildMap.set(guildId, url);
} else if (channelId) {
wallpaperChannelMap.set(channelId, url);
}
store.emitChange();
},
VC_WALLPAPER_FREE_CHANGE_GLOBAL({ url }: { url: string | undefined; }) {
globalDefault = url;
store.emitChange();
},
VC_WALLPAPER_FREE_RESET() {
wallpaperChannelMap.clear();
wallpaperGuildMap.clear();
globalDefault = void 0;
store.emitChange();
}
});
return store;
});

View file

@ -0,0 +1,30 @@
.vc-wpfree-discord-wp-modal {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 24px;
padding: 8px 0;
}
.vc-wpfree-discord-wp-icon-container {
border-radius: 10px;
box-shadow: 0 2px 8px rgb(0 0 0 / 8%);
padding: 16px;
display: flex;
flex-direction: column;
align-items: center
}
.vc-wpfree-discord-wp-icon-img {
width: 120px;
height: 68px;
object-fit: cover;
border-radius: 6px;
margin-bottom: 8px;
border: 1px solid var(--background-modifier-accent);
}
.vc-wpfree-discord-set-buttons {
display: flex;
gap: 8px;
margin-top: 12px
}